From f60cf339d5e1c715eb53636ab52787397ff6578e Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 7 Jul 2024 14:38:39 +0200 Subject: [PATCH 001/162] log manager --- README.md | 30 +-- .../Message/UnitTestRequest.cs | 2 +- .../Schedule/UnitTestCron.cs | 5 +- src/WebExpress.WebCore/HttpServer.cs | 22 +- src/WebExpress.WebCore/HttpServerContext.cs | 5 +- src/WebExpress.WebCore/IHttpServerContext.cs | 3 +- .../Internationalization/de | 3 + .../Internationalization/en | 3 + .../WebComponent/ComponentManager.cs | 10 +- src/WebExpress.WebCore/WebEx.cs | 1 + .../WebExpress.WebCore.csproj | 6 +- .../WebJob/ScheduleStaticItem.cs | 1 + src/WebExpress.WebCore/WebLog/ILog.cs | 236 ++++++++++++++++++ src/WebExpress.WebCore/{ => WebLog}/Log.cs | 86 +++---- .../{ => WebLog}/LogFactory.cs | 2 +- .../{ => WebLog}/LogFrame.cs | 2 +- .../{ => WebLog}/LogFrameSimple.cs | 6 +- .../{ => WebLog}/LogItem.cs | 11 +- src/WebExpress.WebCore/WebLog/LogLevel.cs | 43 ++++ src/WebExpress.WebCore/WebLog/LogManager.cs | 113 +++++++++ src/WebExpress.WebCore/WebLog/LogMode.cs | 23 ++ .../WebModule/ModuleItem.cs | 3 +- .../WebModule/ModuleManager.cs | 2 +- .../WebPage/RenderContext.cs | 1 + .../WebPlugin/PluginManager.cs | 1 + .../WebResource/ResourceItem.cs | 3 +- .../WebSitemap/SitemapManager.cs | 1 + 27 files changed, 525 insertions(+), 99 deletions(-) create mode 100644 src/WebExpress.WebCore/WebLog/ILog.cs rename src/WebExpress.WebCore/{ => WebLog}/Log.cs (89%) rename src/WebExpress.WebCore/{ => WebLog}/LogFactory.cs (96%) rename src/WebExpress.WebCore/{ => WebLog}/LogFrame.cs (98%) rename src/WebExpress.WebCore/{ => WebLog}/LogFrameSimple.cs (86%) rename src/WebExpress.WebCore/{ => WebLog}/LogItem.cs (88%) create mode 100644 src/WebExpress.WebCore/WebLog/LogLevel.cs create mode 100644 src/WebExpress.WebCore/WebLog/LogManager.cs create mode 100644 src/WebExpress.WebCore/WebLog/LogMode.cs diff --git a/README.md b/README.md index 14595ef..c16c145 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,37 @@ -![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) - -# WebExpress -WebExpress is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing +`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net -language (e.g. C#). Some advantages of WebExpress are: +language (e.g. C#). Some advantages of `WebExpress` are: - It is easy to use. - It offers a variety of features and tools that can help you build and manage your website. - It is fast and efficient and can help you save time and money. - It is flexible and can be customized to meet your specific requirements. -The WebExpress family includes the following projects: +The `WebExpress` family includes the following projects: -- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for WebExpress applications and the documentation. -- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for WebExpress applications. -- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for WebExpress applications. -- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for WebExpress applications. -- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for WebExpress applications. +- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for `WebExpress` applications and the documentation. +- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for `WebExpress` applications. +- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for `WebExpress` applications. +- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for `WebExpress` applications. +- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for `WebExpress` applications. # WebExpress.WebCore -WebCore is part of the Webexpres family and includes the basic elements of a WebExpress application. +`WebCore` is part of the `WebExpres` family and includes the basic elements of a `WebExpress` application. # Download The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). # Start -To get started with WebExpress, use the following links and tutorials. +If you're looking to get started with `WebExpress`, we would recommend using the following documentation. It can help you understand the platform. - [installation guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) - [development guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) -## Tutorials +# Learning +The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you’re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. + - [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) +- [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) # Tags -#Raspberry #Raspbian #IoT #NETCore #WebExpress \ No newline at end of file +#WebCore #WebExpress #DotNet #NETCore \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Message/UnitTestRequest.cs b/src/WebExpress.WebCore.Test/Message/UnitTestRequest.cs index d11236c..a37e81c 100644 --- a/src/WebExpress.WebCore.Test/Message/UnitTestRequest.cs +++ b/src/WebExpress.WebCore.Test/Message/UnitTestRequest.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.Test.Message public class UnitTestRequest { /// - /// Creates the fFeature collection. + /// Creates the feature collection. /// /// The reader. /// The request method. diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs index 25b6b0b..c041292 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs @@ -1,8 +1,7 @@ -using System; -using System.Globalization; +using System.Globalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebJob; -using Xunit; +using WebExpress.WebCore.WebLog; namespace WebExpress.WebCore.Test.Schedule { diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 1674dfa..a9d881c 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -20,6 +20,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; @@ -122,7 +123,7 @@ public void Start() serviceCollection.AddMemoryCache(); serviceCollection.AddLogging(x => { - x.SetMinimumLevel(LogLevel.Trace); + x.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); x.AddProvider(logger); }); serviceCollection.AddHttpLogging(x => @@ -151,7 +152,7 @@ public void Start() Kestrel.StartAsync(this, ServerToken); - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.start"), args: new object[] { ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString() }); + HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.start"), args: [ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString()]); Started?.Invoke(this, new EventArgs()); } @@ -171,7 +172,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, End var port = uri.Port; var host = asterisk ? Dns.GetHostEntry(Dns.GetHostName()) : Dns.GetHostEntry(uri.Host); var addressList = host.AddressList - .Union(asterisk ? Dns.GetHostEntry("localhost").AddressList : Array.Empty()) + .Union(asterisk ? Dns.GetHostEntry("localhost").AddressList : []) .Where(x => x.AddressFamily == AddressFamily.InterNetwork || x.AddressFamily == AddressFamily.InterNetworkV6); HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.endpoint"), args: endPoint.Uri); @@ -273,11 +274,13 @@ private Response HandleClient(HttpContext context) if (searchResult != null) { var resourceUri = new UriResource(request.Uri, searchResult.Uri.PathSegments); - resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count())?.PathSegments); - resourceUri.ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments); - resourceUri.ApplicationRoot = new UriResource(request.Uri, searchResult.ApplicationContext?.ContextPath.PathSegments); - resourceUri.ModuleRoot = new UriResource(request.Uri, searchResult.ModuleContext?.ContextPath.PathSegments); - resourceUri.ResourceRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments); + resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count)?.PathSegments) + { + ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments), + ApplicationRoot = new UriResource(request.Uri, searchResult.ApplicationContext?.ContextPath.PathSegments), + ModuleRoot = new UriResource(request.Uri, searchResult.ModuleContext?.ContextPath.PathSegments), + ResourceRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) + }; request.Uri = resourceUri; @@ -393,7 +396,6 @@ private async Task SendResponseAsync(HttpContext context, Response response) responseFeature.StatusCode = response.Status; responseFeature.ReasonPhrase = response.Reason; responseFeature.Headers.KeepAlive = "true"; - responseFeature.Headers.Add("PageID", "Test"); if (response.Header.Location != null) { @@ -415,7 +417,7 @@ private async Task SendResponseAsync(HttpContext context, Response response) responseFeature.Headers.WWWAuthenticate = "Basic realm=\"Bereich\""; } - if (response.Header.Cookies.Any()) + if (response.Header.Cookies.Count != 0) { responseFeature.Headers.SetCookie = string.Join(" ", response.Header.Cookies); } diff --git a/src/WebExpress.WebCore/HttpServerContext.cs b/src/WebExpress.WebCore/HttpServerContext.cs index 1f94c11..c822290 100644 --- a/src/WebExpress.WebCore/HttpServerContext.cs +++ b/src/WebExpress.WebCore/HttpServerContext.cs @@ -2,6 +2,7 @@ using System.Globalization; using System.Reflection; using WebExpress.WebCore.Config; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore @@ -59,7 +60,7 @@ public class HttpServerContext : IHttpServerContext /// /// Returns the log for writing status messages to the console and to a log file. /// - public Log Log { get; protected set; } + public ILog Log { get; protected set; } /// /// Returns the host. @@ -89,7 +90,7 @@ public HttpServerContext string configBaseFolder, UriResource contextPath, CultureInfo culture, - Log log, + ILog log, IHost host ) { diff --git a/src/WebExpress.WebCore/IHttpServerContext.cs b/src/WebExpress.WebCore/IHttpServerContext.cs index 26261a9..de99e2c 100644 --- a/src/WebExpress.WebCore/IHttpServerContext.cs +++ b/src/WebExpress.WebCore/IHttpServerContext.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Globalization; using WebExpress.WebCore.Config; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore @@ -58,7 +59,7 @@ public interface IHttpServerContext /// /// Returns the log for writing status messages to the console and to a log file. /// - Log Log { get; } + ILog Log { get; } /// /// Returns the host. diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 8880cbc..5dc61b0 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -40,6 +40,9 @@ componentmanager.duplicate=Die Komponente '{0}' wurde bereits registriert. componentmanager.remove=Die Komponente '{0}' wurde entfernt. componentmanager.component=Komponenten: +logmanager.initialization:Der Logmanager wurde initialisiert. +logmanager.titel:Logmanager + internationalizationmanager.initialization=Der Internationalisierungsmanager wurde initialisiert. internationalizationmanager.register=Das Plugin '{0}' wurde im Internationalizationmanager registriert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 164d882..73c4def 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -40,6 +40,9 @@ componentmanager.duplicate=The component '{0}' has already been registered. componentmanager.remove=The component '{0}' has been removed. componentmanager.component=Components: +logmanager.initialization:The log manager has been initialized. +logmanager.titel:Log manager + internationalizationmanager.initialization=The internationalization manager has been initialized. internationalizationmanager.register=The plugin '{0}' is registered in the internationalization manager. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index 4483349..1d8f35c 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -6,6 +6,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebJob; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPackage; using WebExpress.WebCore.WebPlugin; @@ -60,6 +61,12 @@ public static class ComponentManager TaskManager }.Concat(Dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); + /// + /// Returns the log manager. + /// + /// The instance of the log manager or null. + public static LogManager LogManager { get; private set; } + /// /// Returns the package manager. /// @@ -148,6 +155,7 @@ internal static void Initialization(IHttpServerContext httpServerContext) ); // order is relevant + LogManager = CreateInstance(typeof(LogManager)) as LogManager; PackageManager = CreateInstance(typeof(PackageManager)) as PackageManager; PluginManager = CreateInstance(typeof(PluginManager)) as PluginManager; InternationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; @@ -211,8 +219,6 @@ private static IComponent CreateInstance(Type componentType) null ) as IComponent; - //var component = Activator.CreateInstance(componentType, flags) as IComponent; - component.Initialization(HttpServerContext); return component; diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index cbf3106..4e7e55d 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; [assembly: InternalsVisibleTo("WebExpress.WebCore.Test")] diff --git a/src/WebExpress.WebCore/WebExpress.WebCore.csproj b/src/WebExpress.WebCore/WebExpress.WebCore.csproj index 52fc48c..57ef2f9 100644 --- a/src/WebExpress.WebCore/WebExpress.WebCore.csproj +++ b/src/WebExpress.WebCore/WebExpress.WebCore.csproj @@ -3,8 +3,8 @@ Library WebExpress.WebCore - 0.0.7.0 - 0.0.7.0 + 0.0.8.0 + 0.0.8.0 net8.0 any https://github.com/ReneSchwarzer/WebExpress.git @@ -14,7 +14,7 @@ true True Core library of the WebExpress web server. - 0.0.7-alpha + 0.0.8-alpha https://github.com/ReneSchwarzer/WebExpress icon.png README.md diff --git a/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs b/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs index 8e8c4ff..ee86178 100644 --- a/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs +++ b/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/WebLog/ILog.cs b/src/WebExpress.WebCore/WebLog/ILog.cs new file mode 100644 index 0000000..427634e --- /dev/null +++ b/src/WebExpress.WebCore/WebLog/ILog.cs @@ -0,0 +1,236 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Runtime.CompilerServices; +using System.Text; +using WebExpress.WebCore.Setting; + +namespace WebExpress.WebCore.WebLog +{ + /// + /// Interface for logging events to your log file + /// + /// The program writes a variety of information to an event log file. The log + /// is stored in the log directory. The name consists of the date and the ending ".log". + /// The structure is designed in such a way that the log file can be read and analyzed with a text editor. + /// Error messages and notes are made available persistently in the log, so the event log files + /// are suitable for error analysis and for checking the correct functioning of the program. The minutes + /// are organized in tabular form. In the first column, the primeval time is indicated. The second + /// column defines the level of the log entry. The third column lists the function that produced the entry. + /// The last column indicates a note or error description. + /// + /// + /// Example:
+ /// 08:26:30 Info Program.Main Startup
+ /// 08:26:30 Info Program.Main --------------------------------------------------
+ /// 08:26:30 Info Program.Main Version: 0.0.0.1
+ /// 08:26:30 Info Program.Main Arguments: -test
+ /// 08:26:30 Info Program.Main Configuration version: V1
+ /// 08:26:30 Info Program.Main Processing: sequentiell
+ ///
+ public interface ILog : ILogger + { + /// + /// Returns or sets the encoding. + /// + public Encoding Encoding { get; set; } + + /// + /// Determines whether to display debug output. + /// + public bool DebugMode { get; } + + /// + /// Returns the file name of the log + /// + public string Filename { get; set; } + + /// + /// Returns the number of exceptions. + /// + public int ExceptionCount { get; } + + /// + /// Returns the number of errors (errors + exceptions). + /// + public int ErrorCount { get; } + + /// + /// Returns the number of warnings. + /// + public int WarningCount { get; } + + /// + /// Checks if the log has been opened for writing. + /// + public bool IsOpen { get; } + + /// + /// Returns the log mode. + /// + public LogMode LogMode { get; set; } + + /// + /// The default instance of the logger. + /// + public static Log Current { get; } + + /// + /// Set file name time patterns. + /// + public string FilePattern { set; get; } + + /// + /// Time patternsspecifying log entries. + /// + public string TimePattern { set; get; } + + /// + /// Starts logging + /// + /// The path where the log file is created. + /// The file name of the log file. + public void Begin(string path, string name); + + /// + /// Starts logging + /// + /// The path where the log file is created. + public void Begin(string path); + + /// + /// Starts logging + /// + /// The log settings + public void Begin(SettingLogItem settings); + + /// + /// A dividing line with * characters + /// + public void Seperator(); + + /// + /// A separator with custom characters + /// + /// The separator. + public void Seperator(char sepChar); + + /// + /// Logs an info message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void Info(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs an info message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + /// Parameter für die Formatierung der Nachricht + public void Info(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null, params object[] args); + + /// + /// Logs a warning message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void Warning(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs a warning message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + /// Parameter für die Formatierung der Nachricht + public void Warning(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null, params object[] args); + + /// + /// Logs an error message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void Error(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs an error message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + /// Parameter für die Formatierung der Nachricht + public void Error(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null, params object[] args); + + /// + /// Logs an error message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void FatalError(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs an error message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + /// Parameter für die Formatierung der Nachricht + public void FatalError(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null, params object[] args); + + /// + /// Logs an exception message. + /// + /// The exception + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void Exception(Exception ex, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs a debug message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + public void Debug(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null); + + /// + /// Logs a debug message. + /// + /// The log message. + /// >Method/ function that wants to log. + /// The line number. + /// The source file. + /// Parameter für die Formatierung der Nachricht + public void Debug(string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null, params object[] args); + + /// + /// Stops logging. + /// + public void Close(); + + /// + /// Cleans up the log. + /// + public void Clear(); + + /// + /// Writes the contents of the queue to the log. + /// + public void Flush(); + } +} diff --git a/src/WebExpress.WebCore/Log.cs b/src/WebExpress.WebCore/WebLog/Log.cs similarity index 89% rename from src/WebExpress.WebCore/Log.cs rename to src/WebExpress.WebCore/WebLog/Log.cs index 8f3eab7..5df94ae 100644 --- a/src/WebExpress.WebCore/Log.cs +++ b/src/WebExpress.WebCore/WebLog/Log.cs @@ -8,7 +8,7 @@ using System.Threading; using WebExpress.WebCore.Setting; -namespace WebExpress.WebCore +namespace WebExpress.WebCore.WebLog { /// /// Class for logging events to your log file @@ -31,18 +31,8 @@ namespace WebExpress.WebCore /// 08:26:30 Info Program.Main Configuration version: V1
/// 08:26:30 Info Program.Main Processing: sequentiell
/// - public class Log : ILogger + public class Log : ILog { - /// - /// Enumeration defines the different log levels. - /// - public enum Level { Info, Warning, FatalError, Error, Exception, Debug, Seperartor }; - - /// - /// Enumerations of the log mode. - /// - public enum Mode { Off, Append, Override }; - /// /// Returns or sets the encoding. /// @@ -81,13 +71,23 @@ public enum Mode { Off, Append, Override }; /// /// Returns the log mode. /// - public Mode LogMode { get; set; } + public LogMode LogMode { get; set; } /// /// The default instance of the logger. /// public static Log Current { get; } = new Log(); + /// + /// Set file name time patterns. + /// + public string FilePattern { set; get; } + + /// + /// Time patternsspecifying log entries. + /// + public string TimePattern { set; get; } + /// /// The directory where the log is created. /// @@ -140,7 +140,7 @@ public Log() Encoding = Encoding.UTF8; FilePattern = "yyyyMMdd"; TimePattern = "yyyMMddHHmmss"; - LogMode = Log.Mode.Append; + LogMode = LogMode.Append; } /// @@ -161,7 +161,7 @@ public void Begin(string path, string name) } // Delete existing log file when overwrite mode is active - if (LogMode == Mode.Override) + if (LogMode == LogMode.Override) { try { @@ -199,7 +199,7 @@ public void Begin(string path) public void Begin(SettingLogItem settings) { Filename = settings.Filename; - LogMode = (Mode)Enum.Parse(typeof(Mode), settings.Modus); + LogMode = (LogMode)Enum.Parse(typeof(LogMode), settings.Modus); Encoding = Encoding.GetEncoding(settings.Encoding); TimePattern = settings.Timepattern; DebugMode = settings.Debug; @@ -215,7 +215,7 @@ public void Begin(SettingLogItem settings) /// Method/ function that wants to log. /// The line number. /// The source file. - protected virtual void Add(Level level, string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null) + protected virtual void Add(LogLevel level, string message, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null) { foreach (var l in message?.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { @@ -224,12 +224,12 @@ protected virtual void Add(Level level, string message, [CallerMemberName] strin var item = new LogItem(level, instance, l, TimePattern); switch (level) { - case Level.Error: - case Level.FatalError: - case Level.Exception: + case LogLevel.Error: + case LogLevel.FatalError: + case LogLevel.Exception: Console.ForegroundColor = ConsoleColor.Red; break; - case Level.Warning: + case LogLevel.Warning: Console.ForegroundColor = ConsoleColor.Yellow; break; default: @@ -258,7 +258,7 @@ public void Seperator() /// The separator. public void Seperator(char sepChar) { - Add(Level.Seperartor, "".PadRight(_seperatorWidth, sepChar)); + Add(LogLevel.Seperartor, "".PadRight(_seperatorWidth, sepChar)); } /// @@ -273,7 +273,7 @@ public void Info(string message, [CallerMemberName] string instance = null, [Cal var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Info, message, $"{className}.{instance}", line, file); + Add(LogLevel.Info, message, $"{className}.{instance}", line, file); } /// @@ -289,7 +289,7 @@ public void Info(string message, [CallerMemberName] string instance = null, [Cal var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Info, string.Format(message, args), $"{className}.{instance}", line, file); + Add(LogLevel.Info, string.Format(message, args), $"{className}.{instance}", line, file); } /// @@ -304,7 +304,7 @@ public void Warning(string message, [CallerMemberName] string instance = null, [ var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Warning, message, $"{className}.{instance}", line, file); + Add(LogLevel.Warning, message, $"{className}.{instance}", line, file); WarningCount++; } @@ -322,7 +322,7 @@ public void Warning(string message, [CallerMemberName] string instance = null, [ var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Warning, string.Format(message, args), $"{className}.{instance}", line, file); + Add(LogLevel.Warning, string.Format(message, args), $"{className}.{instance}", line, file); WarningCount++; } @@ -339,7 +339,7 @@ public void Error(string message, [CallerMemberName] string instance = null, [Ca var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Error, message, $"{className}.{instance}", line, file); + Add(LogLevel.Error, message, $"{className}.{instance}", line, file); ErrorCount++; } @@ -357,7 +357,7 @@ public void Error(string message, [CallerMemberName] string instance = null, [Ca var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.Error, string.Format(message, args), $"{className}.{instance}", line, file); + Add(LogLevel.Error, string.Format(message, args), $"{className}.{instance}", line, file); ErrorCount++; } @@ -374,7 +374,7 @@ public void FatalError(string message, [CallerMemberName] string instance = null var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.FatalError, message, $"{className}.{instance}", line, file); + Add(LogLevel.FatalError, message, $"{className}.{instance}", line, file); ErrorCount++; } @@ -392,7 +392,7 @@ public void FatalError(string message, [CallerMemberName] string instance = null var methodInfo = new StackTrace().GetFrame(1).GetMethod(); var className = methodInfo.ReflectedType.Name; - Add(Level.FatalError, string.Format(message, args), $"{className}.{instance}", line, file); + Add(LogLevel.FatalError, string.Format(message, args), $"{className}.{instance}", line, file); ErrorCount++; } @@ -411,8 +411,8 @@ public void Exception(Exception ex, [CallerMemberName] string instance = null, [ lock (_queue) { - Add(Level.Exception, ex?.Message.Trim(), $"{className}.{instance}", line, file); - Add(Level.Exception, ex?.StackTrace != null ? ex?.StackTrace.Trim() : ex?.Message.Trim(), $"{className}.{instance}", line, file); + Add(LogLevel.Exception, ex?.Message.Trim(), $"{className}.{instance}", line, file); + Add(LogLevel.Exception, ex?.StackTrace != null ? ex?.StackTrace.Trim() : ex?.Message.Trim(), $"{className}.{instance}", line, file); ExceptionCount++; ErrorCount++; @@ -433,7 +433,7 @@ public void Debug(string message, [CallerMemberName] string instance = null, [Ca if (DebugMode) { - Add(Level.Debug, message, $"{className}.{instance}", line, file); + Add(LogLevel.Debug, message, $"{className}.{instance}", line, file); } } @@ -452,7 +452,7 @@ public void Debug(string message, [CallerMemberName] string instance = null, [Ca if (DebugMode) { - Add(Level.Debug, string.Format(message, args), $"{className}.{instance}", line, file); + Add(LogLevel.Debug, string.Format(message, args), $"{className}.{instance}", line, file); } } @@ -495,7 +495,7 @@ public void Flush() } // protect file writing from concurrent access - if (list.Count > 0 && LogMode != Mode.Off) + if (list.Count > 0 && LogMode != LogMode.Off) { lock (_path) { @@ -538,9 +538,9 @@ private void ThreadProc() /// The entry to write. Can also be an object. /// The exception that applies to this entry. /// Function to create a string message of the state and exception parameters. - void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + void ILogger.Log(Microsoft.Extensions.Logging.LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - if (logLevel == LogLevel.Error) + if (logLevel == Microsoft.Extensions.Logging.LogLevel.Error) { var message = exception?.Message ?? formatter.Invoke(state, exception); Error(message, "Kestrel", null, null); @@ -553,7 +553,7 @@ void ILogger.Log(LogLevel logLevel, EventId eventId, TState state, Excep /// /// Level to be checked. /// True in the enabled state, false otherwise. - public bool IsEnabled(LogLevel logLevel) + public bool IsEnabled(Microsoft.Extensions.Logging.LogLevel logLevel) { return true; } @@ -568,15 +568,5 @@ public IDisposable BeginScope(TState state) { return null; } - - /// - /// Set file name time patterns. - /// - public string FilePattern { set; get; } - - /// - /// Time patternsspecifying log entries. - /// - public string TimePattern { set; get; } } } diff --git a/src/WebExpress.WebCore/LogFactory.cs b/src/WebExpress.WebCore/WebLog/LogFactory.cs similarity index 96% rename from src/WebExpress.WebCore/LogFactory.cs rename to src/WebExpress.WebCore/WebLog/LogFactory.cs index abfafc3..3b531f5 100644 --- a/src/WebExpress.WebCore/LogFactory.cs +++ b/src/WebExpress.WebCore/WebLog/LogFactory.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.Logging; -namespace WebExpress.WebCore +namespace WebExpress.WebCore.WebLog { public class LogFactory : ILoggerFactory, ILoggerProvider { diff --git a/src/WebExpress.WebCore/LogFrame.cs b/src/WebExpress.WebCore/WebLog/LogFrame.cs similarity index 98% rename from src/WebExpress.WebCore/LogFrame.cs rename to src/WebExpress.WebCore/WebLog/LogFrame.cs index 8ea8397..c90d3a2 100644 --- a/src/WebExpress.WebCore/LogFrame.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrame.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace WebExpress.WebCore +namespace WebExpress.WebCore.WebLog { /// /// Creates a frame of log entries. diff --git a/src/WebExpress.WebCore/LogFrameSimple.cs b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs similarity index 86% rename from src/WebExpress.WebCore/LogFrameSimple.cs rename to src/WebExpress.WebCore/WebLog/LogFrameSimple.cs index 76a4028..6c049c1 100644 --- a/src/WebExpress.WebCore/LogFrameSimple.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.CompilerServices; -namespace WebExpress.WebCore +namespace WebExpress.WebCore.WebLog { /// /// Creates a frame of log entries. @@ -26,7 +26,7 @@ public class LogFrameSimple : IDisposable /// /// The log entry. /// - protected Log Log { get; set; } + protected ILog Log { get; set; } /// /// Constructor @@ -35,7 +35,7 @@ public class LogFrameSimple : IDisposable /// Method that wants to log. /// The line number. /// The source file. - public LogFrameSimple(Log log, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null) + public LogFrameSimple(ILog log, [CallerMemberName] string instance = null, [CallerLineNumber] int? line = null, [CallerFilePath] string file = null) { Instance = instance; Log = log; diff --git a/src/WebExpress.WebCore/LogItem.cs b/src/WebExpress.WebCore/WebLog/LogItem.cs similarity index 88% rename from src/WebExpress.WebCore/LogItem.cs rename to src/WebExpress.WebCore/WebLog/LogItem.cs index d92b38d..3cd261d 100644 --- a/src/WebExpress.WebCore/LogItem.cs +++ b/src/WebExpress.WebCore/WebLog/LogItem.cs @@ -1,7 +1,6 @@ using System; -using static WebExpress.WebCore.Log; -namespace WebExpress.WebCore +namespace WebExpress.WebCore.WebLog { /// /// Log entry @@ -11,7 +10,7 @@ internal class LogItem /// /// Level of the entry. /// - private readonly Level m_level; + private readonly LogLevel m_level; /// /// The instance (location). @@ -34,7 +33,7 @@ internal class LogItem /// The level. /// The modul/funktion. /// The log message. - public LogItem(Level level, string instance, string message, string timePattern) + public LogItem(LogLevel level, string instance, string message, string timePattern) { m_level = level; m_instance = instance; @@ -49,7 +48,7 @@ public LogItem(Level level, string instance, string message, string timePattern) /// The log entry as a string public override string ToString() { - if (m_level != Level.Seperartor) + if (m_level != LogLevel.Seperartor) { return m_timestamp.ToString(TimePattern) + " " + m_level.ToString().PadRight(9, ' ') + " " + m_instance.PadRight(19, ' ').Substring(0, 19) + " " + m_message; } @@ -62,7 +61,7 @@ public override string ToString() /// /// Returns the level of the entry. /// - public Level Level => m_level; + public LogLevel Level => m_level; /// /// Returns the instance (location). diff --git a/src/WebExpress.WebCore/WebLog/LogLevel.cs b/src/WebExpress.WebCore/WebLog/LogLevel.cs new file mode 100644 index 0000000..3406197 --- /dev/null +++ b/src/WebExpress.WebCore/WebLog/LogLevel.cs @@ -0,0 +1,43 @@ +namespace WebExpress.WebCore.WebLog +{ + /// + /// Enumeration defines the different log levels. + /// + public enum LogLevel + { + /// + /// An informational message. This is generally used for informational messages that display the progress of the application at coarse-grained level. + /// + Info, + + /// + /// A warning message. This is typically for situations that are not necessarily errors, but that may need attention during debugging or tuning. + /// + Warning, + + /// + /// A fatal error message. This is a very severe error event that will presumably lead the application to abort. + /// + FatalError, + + /// + /// An error message. This is a message indicating that an operation could not be completed. + /// + Error, + + /// + /// An exception message. This is a message indicating that an exception was thrown by an operation. + /// + Exception, + + /// + /// A debug message. This is a message containing low-level information for developers and system administrators for debugging purposes. + /// + Debug, + + /// + /// A separator message. This is used to separate groups of messages in the logging output. + /// + Seperartor + } +} diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs new file mode 100644 index 0000000..5b40533 --- /dev/null +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebLog +{ + public class LogManager : IComponentPlugin, ISystemComponent + { + /// + /// An event that fires when an log is added. + /// + public event EventHandler AddLog; + + /// + /// An event that fires when an log is removed. + /// + + public event EventHandler RemoveLog; + + /// + /// Returns or sets the reference to the context of the host. + /// + public IHttpServerContext HttpServerContext { get; private set; } + + /// + /// Returns the default log. + /// + public ILog DefaultLog => HttpServerContext.Log; + + /// + /// Initializes a new instance of the class. + /// + internal LogManager() + { + } + + /// + /// Initialization + /// + /// The reference to the context of the host. + public void Initialization(IHttpServerContext context) + { + HttpServerContext = context; + + HttpServerContext.Log.Debug + ( + InternationalizationManager.I18N("webexpress:logmanager.initialization") + ); + } + + /// + /// Discovers and registers logs from the specified plugin. + /// + /// A context of a plugin whose logs are to be registered. + public void Register(IPluginContext pluginContext) + { + throw new System.NotImplementedException(); + } + + /// + /// Discovers and registers logs from the specified plugin. + /// + /// A list with plugin contexts that contain the logs. + public void Register(IEnumerable pluginContexts) + { + throw new System.NotImplementedException(); + } + + /// + /// Removes all logs associated with the specified plugin context. + /// + /// The context of the plugin that contains the log to remove. + public void Remove(IPluginContext pluginContext) + { + throw new System.NotImplementedException(); + } + + /// + /// Raises the AddLog event. + /// + /// The plugin context. + private void OnAddModule(IPluginContext pluginContext) + { + AddLog?.Invoke(this, pluginContext); + } + + /// + /// Raises the RemoveLog event. + /// + /// The plugin context. + private void OnRemoveModule(IPluginContext pluginContext) + { + RemoveLog?.Invoke(this, pluginContext); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + /// The context of the plugin. + /// A list of log entries. + /// The shaft deep. + public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + { + output.Add + ( + string.Empty.PadRight(deep) + + InternationalizationManager.I18N("webexpress:logmanager.titel") + ); + } + } +} diff --git a/src/WebExpress.WebCore/WebLog/LogMode.cs b/src/WebExpress.WebCore/WebLog/LogMode.cs new file mode 100644 index 0000000..1b466a6 --- /dev/null +++ b/src/WebExpress.WebCore/WebLog/LogMode.cs @@ -0,0 +1,23 @@ +namespace WebExpress.WebCore.WebLog +{ + /// + /// Enumerations of the log mode. + /// + public enum LogMode + { + /// + /// Logging is turned off. + /// + Off, + + /// + /// Logs are appended to any existing logs. + /// + Append, + + /// + /// Any existing logs are overridden. + /// + Override + } +} diff --git a/src/WebExpress.WebCore/WebModule/ModuleItem.cs b/src/WebExpress.WebCore/WebModule/ModuleItem.cs index 3f3e1fb..c161fe6 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleItem.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleItem.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -75,7 +76,7 @@ internal class ModuleItem : IDisposable /// /// Returns the log to write status messages to the console and to a log file. /// - public Log Log { get; internal set; } + public ILog Log { get; internal set; } /// /// Returns the directory where the module instances are listed. diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index ec4a6e1..fe9dced 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -44,7 +44,7 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst .Select(x => x.ModuleContext); /// - /// Constructor + /// Initializes a new instance of the class. /// internal ModuleManager() { diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 97b3672..248bfc7 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,6 +1,7 @@ using System.Globalization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index a1659ce..3cd3427 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -7,6 +7,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPlugin diff --git a/src/WebExpress.WebCore/WebResource/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/ResourceItem.cs index ae43196..d83b41f 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceItem.cs @@ -4,6 +4,7 @@ using System.Text.RegularExpressions; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; @@ -94,7 +95,7 @@ public class ResourceItem : IDisposable /// /// Returns the log to write status messages to the console and to a log file. /// - public Log Log { get; internal set; } + public ILog Log { get; internal set; } /// /// Returns the directory where the module instances are listed. diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 6771810..937c211 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -4,6 +4,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; From 9eb34e413a3750d1406794967963bfd313366458 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 8 Jul 2024 21:41:03 +0200 Subject: [PATCH 002/162] quality improvements --- .../Uri/UnitTestUriAbsolute.cs | 30 ++++++++----------- .../InternationalizationManager.cs | 14 ++++++--- .../WebComponent/ComponentManager.cs | 7 +++-- .../WebComponent/IComponent.cs | 4 +-- .../WebPlugin/PluginManager.cs | 8 ++--- 5 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs index 175552a..a001e5b 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; -using System.Linq; -using WebExpress.WebCore.WebUri; -using Xunit; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -16,20 +13,17 @@ public void Test_0() var str = "http://user@example.com:8080/abc#a?b=1&c=2"; var uri = new UriResource(str); - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.Authority.User == "user" && - uri.Authority.Host == "example.com" && - uri.Authority.Port == 8080 && - uri.Fragment == "a" && - uri.Query.FirstOrDefault()?.Key == "b" && - uri.Query.FirstOrDefault()?.Value == "1" && - uri.Query.LastOrDefault()?.Key == "c" && - uri.Query.LastOrDefault()?.Value == "2" && - uri.IsRelative == false - ); + Assert.Equal(uri.ToString(), str); + Assert.Equal(UriScheme.Http, uri.Scheme); + Assert.Equal("user", uri.Authority.User); + Assert.Equal("example.com", uri.Authority.Host); + Assert.Equal(8080, uri.Authority.Port); + Assert.Equal("a", uri.Fragment); + Assert.Equal("b", uri.Query.FirstOrDefault()?.Key); + Assert.Equal("1", uri.Query.FirstOrDefault()?.Value); + Assert.Equal("c", uri.Query.LastOrDefault()?.Key); + Assert.Equal("2", uri.Query.LastOrDefault()?.Value); + Assert.False(uri.IsRelative); } [Fact] diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index d582a8a..13d8a76 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -3,8 +3,8 @@ using System.IO; using System.Linq; using System.Reflection; -using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Internationalization @@ -192,11 +192,17 @@ public static string I18N(CultureInfo culture, string pluginId, string key) language = DefaultCulture?.TwoLetterISOLanguageName; } - var item = Dictionary[language]; + if (string.IsNullOrWhiteSpace(language)) + { + return key; + } - if (item.ContainsKey(k)) + if (Dictionary.TryGetValue(language, out InternationalizationItem item)) { - return item[k]; + if (item.TryGetValue(k, out string value)) + { + return value; + } } return key; diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index 1d8f35c..e7fac69 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -48,6 +48,7 @@ public static class ComponentManager /// public static IEnumerable Components => new IComponent[] { + LogManager, PackageManager, PluginManager, ApplicationManager, @@ -369,7 +370,7 @@ internal static void Execute() /// /// Shutting down the component manager. /// - public static void ShutDown() + internal static void ShutDown() { HttpServerContext.Log.Debug ( @@ -381,7 +382,7 @@ public static void ShutDown() /// Shutting down the component. /// /// The plugin context. - public static void ShutDownComponent(IPluginContext pluginContext) + internal static void ShutDownComponent(IPluginContext pluginContext) { PluginManager.ShutDown(pluginContext); ApplicationManager.ShutDown(pluginContext); @@ -451,7 +452,7 @@ private static void OnRemoveComponent(IComponent component) /// /// Output of the components to the log. /// - public static void LogStatus() + internal static void LogStatus() { using var frame = new LogFrameSimple(HttpServerContext.Log); var output = new List diff --git a/src/WebExpress.WebCore/WebComponent/IComponent.cs b/src/WebExpress.WebCore/WebComponent/IComponent.cs index f4228c8..fc6ffc4 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponent.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponent.cs @@ -16,8 +16,8 @@ public interface IComponent /// /// Initialization /// - /// The reference to the context of the host. - void Initialization(IHttpServerContext context); + /// The reference to the context of the host. + void Initialization(IHttpServerContext httpServerContext); /// /// Information about the component is collected and prepared for output in the log. diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 3cd3427..d1a8484 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -45,7 +45,7 @@ public class PluginManager : IComponent, IExecutableElements, ISystemComponent /// /// Returns all plugins. /// - public ICollection Plugins => Dictionary.Values.Select(x => x.PluginContext).ToList(); + public IEnumerable Plugins => Dictionary.Values.Select(x => x.PluginContext).ToList(); /// /// Constructor @@ -369,15 +369,15 @@ private bool HasUnfulfilledDependencies(string id, IEnumerable dependenc /// /// Returns a plugin context based on its id. /// - /// The id of the plugin. + /// The id of the plugin. /// The plugin context. - public IPluginContext GetPlugin(string id) + public IPluginContext GetPlugin(string pluginId) { return Dictionary.Values .Where ( x => x.PluginContext != null && - x.PluginContext.PluginId.Equals(id, StringComparison.OrdinalIgnoreCase) + x.PluginContext.PluginId.Equals(pluginId, StringComparison.OrdinalIgnoreCase) ) .Select(x => x.PluginContext) .FirstOrDefault(); From f9116049992fe0fa4e9d9412b4076c92b7e6754d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 8 Jul 2024 23:32:00 +0200 Subject: [PATCH 003/162] quality improvements --- .../WebComponent/ComponentManager.cs | 6 +++--- src/WebExpress.WebCore/WebPage/RenderContext.cs | 3 +-- src/WebExpress.WebCore/WebPlugin/PluginItem.cs | 2 +- src/WebExpress.WebCore/WebPlugin/PluginManager.cs | 12 ++++++------ 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index e7fac69..776d6a8 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -41,7 +41,7 @@ public static class ComponentManager /// /// Returns the directory where the components are listed. /// - private static ComponentDictionary Dictionary { get; } = new ComponentDictionary(); + private static ComponentDictionary Dictionary { get; } = []; /// /// Returns all registered components. @@ -264,7 +264,7 @@ public static T GetComponent() where T : IComponent /// Discovers and registers the components from the specified plugin. /// /// A plugin context that contain the components. - public static void Register(IPluginContext pluginContext) + internal static void Register(IPluginContext pluginContext) { // the plugin has already been registered if (Dictionary.ContainsKey(pluginContext)) @@ -274,7 +274,7 @@ public static void Register(IPluginContext pluginContext) var assembly = pluginContext.Assembly; - Dictionary.Add(pluginContext, new List()); + Dictionary.Add(pluginContext, []); var componentItems = Dictionary[pluginContext]; foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IComponent).Name) != null)) diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 248bfc7..0fe00a2 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -3,7 +3,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage @@ -77,7 +76,7 @@ public RenderContext(IPage page, Request request, IVisualTree visualTree) Page = page; Request = request; VisualTree = visualTree; - Culture = (Page as Resource).Culture; + Culture = Page.Culture; } /// diff --git a/src/WebExpress.WebCore/WebPlugin/PluginItem.cs b/src/WebExpress.WebCore/WebPlugin/PluginItem.cs index e448238..775eb71 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginItem.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginItem.cs @@ -32,7 +32,7 @@ internal class PluginItem /// /// The dependencies of the plugin. /// - public IEnumerable Dependencies { get; internal set; } = new List(); + public IEnumerable Dependencies { get; internal set; } = []; /// /// Thread termination token. diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index d1a8484..3855632 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -35,12 +35,12 @@ public class PluginManager : IComponent, IExecutableElements, ISystemComponent /// /// Returns the directory where the plugins are listed. /// - private PluginDictionary Dictionary { get; } = new PluginDictionary(); + private PluginDictionary Dictionary { get; } = []; /// /// Plugins that do not meet the dependencies. /// - private PluginDictionary UnfulfilledDependencies { get; } = new PluginDictionary(); + private PluginDictionary UnfulfilledDependencies { get; } = []; /// /// Returns all plugins. @@ -392,7 +392,7 @@ private PluginItem GetPluginItem(IPluginContext pluginContext) { var pluginId = pluginContext?.PluginId?.ToLower(); - if (pluginId == null || !Dictionary.ContainsKey(pluginId)) + if (pluginId == null || !Dictionary.TryGetValue(pluginId, out PluginItem value)) { HttpServerContext.Log.Warning ( @@ -406,7 +406,7 @@ private PluginItem GetPluginItem(IPluginContext pluginContext) return null; } - return Dictionary[pluginId]; + return value; } /// @@ -477,7 +477,7 @@ internal void Boot(IEnumerable contexts) /// Shut down the plugin. /// /// The context of the plugin to shut down. - public void ShutDown(IPluginContext pluginContext) + internal void ShutDown(IPluginContext pluginContext) { var plugin = GetPluginItem(pluginContext); @@ -489,7 +489,7 @@ public void ShutDown(IPluginContext pluginContext) /// Shut down the plugins. /// /// A list of contexts of plugins to shut down. - public void ShutDown(IEnumerable contexts) + internal void ShutDown(IEnumerable contexts) { foreach (var context in contexts) { From b75cfb6f4285052b361546bece7d9cd9d3888230 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 9 Jul 2024 21:21:15 +0200 Subject: [PATCH 004/162] bug fixes --- src/WebExpress.WebCore/WebMessage/Request.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index 9e2e08f..adbeb2e 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -451,9 +451,9 @@ private void ParseRequestParams() /// private void ParseSessionParams() { - Session = ComponentManager.SessionManager.GetSession(this); + Session = ComponentManager.SessionManager?.GetSession(this); - var property = Session.GetProperty(); + var property = Session?.GetProperty(); if (property != null && property.Params != null) { foreach (var param in property.Params) From 4c33e683b1e4c2a167e454ce7dc2d9287b17b578 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 9 Jul 2024 23:45:57 +0200 Subject: [PATCH 005/162] add tests --- .../Html/UnitTestHtmlElementFieldLabel.cs | 127 +++++++++++++++++ .../Html/UnitTestHtmlElementTextContentP.cs | 128 ++++++++++++++++++ .../Html/UnitTestHtmlText.cs | 46 +++++++ .../IHtmlNodeExtensions.cs | 24 ++++ .../WebExpress.WebCore.Test.csproj | 2 +- src/WebExpress.WebCore/WebHtml/HtmlElement.cs | 2 +- 6 files changed, 327 insertions(+), 2 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs create mode 100644 src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs create mode 100644 src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs create mode 100644 src/WebExpress.WebCore.Test/IHtmlNodeExtensions.cs diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs new file mode 100644 index 0000000..48c80d4 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs @@ -0,0 +1,127 @@ +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test.Html +{ + public class UnitTestHtmlElementFieldLabel + { + /// + /// Tests a empty tag. + /// + [Fact] + public void Empty() + { + // test execution + var html = new HtmlElementFieldLabel(); + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtInstancing() + { + // test execution + var html = new HtmlElementFieldLabel("abcdef"); + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtProperty() + { + // test execution + var html = new HtmlElementFieldLabel + { + Text = "abcdef" + }; + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtHtmlText() + { + // test execution + var html = new HtmlElementFieldLabel(new HtmlText("abc"), new HtmlText("def")); + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextWithId() + { + // test execution + var html = new HtmlElementFieldLabel() + { + Id = "identity" + }; + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Inline() + { + // test execution + var html = new HtmlElementFieldLabel(); + + Assert.False(html.Inline); + } + + /// + /// Tests a tag. + /// + [Fact] + public void CloseTag() + { + // test execution + var html = new HtmlElementFieldLabel(); + + Assert.True(html.CloseTag); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Class() + { + // test execution + var html = new HtmlElementFieldLabel() + { + Class = "abc" + }; + + Assert.Equal(@"", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Style() + { + // test execution + var html = new HtmlElementFieldLabel() + { + Style = "abc" + }; + + Assert.Equal(@"", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs new file mode 100644 index 0000000..ad0ee7e --- /dev/null +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs @@ -0,0 +1,128 @@ +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test.Html +{ + public class UnitTestHtmlElementTextContentP + { + /// + /// Tests a empty tag. + /// + [Fact] + public void Empty() + { + // test execution + var html = new HtmlElementTextContentP(); + + Assert.Equal(@"

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtInstancing() + { + // test execution + var html = new HtmlElementTextContentP("abcdef"); + + Assert.Equal(@"

abcdef

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtProperty() + { + // test execution + var html = new HtmlElementTextContentP + { + Text = "abcdef" + }; + + Assert.Equal(@"

abcdef

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtHtmlText() + { + // test execution + var html = new HtmlElementTextContentP(new HtmlText("abc"), new HtmlText("def")); + var str = html.ToString(); + + Assert.Equal(@"

abcdef

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextWithId() + { + // test execution + var html = new HtmlElementTextContentP() + { + Id = "identity" + }; + + Assert.Equal(@"

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Inline() + { + // test execution + var html = new HtmlElementTextContentP(); + + Assert.False(html.Inline); + } + + /// + /// Tests a tag. + /// + [Fact] + public void CloseTag() + { + // test execution + var html = new HtmlElementTextContentP(); + + Assert.True(html.CloseTag); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Class() + { + // test execution + var html = new HtmlElementTextContentP() + { + Class = "abc" + }; + + Assert.Equal(@"

", html.Trim()); + } + + /// + /// Tests a tag. + /// + [Fact] + public void Style() + { + // test execution + var html = new HtmlElementTextContentP() + { + Style = "abc" + }; + + Assert.Equal(@"

", html.Trim()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs new file mode 100644 index 0000000..e5bf608 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs @@ -0,0 +1,46 @@ +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test.Html +{ + public class UnitTestHtmlText + { + /// + /// Tests a empty tag. + /// + [Fact] + public void Empty() + { + // test execution + var html = new HtmlText(); + + Assert.Null(html.Value); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtInstancing() + { + // test execution + var html = new HtmlText("abcdef"); + + Assert.Equal(@"abcdef", html.Value); + } + + /// + /// Tests a tag. + /// + [Fact] + public void TextAtProperty() + { + // test execution + var html = new HtmlText + { + Value = "abcdef" + }; + + Assert.Equal(@"abcdef", html.Value); + } + } +} diff --git a/src/WebExpress.WebCore.Test/IHtmlNodeExtensions.cs b/src/WebExpress.WebCore.Test/IHtmlNodeExtensions.cs new file mode 100644 index 0000000..3da4116 --- /dev/null +++ b/src/WebExpress.WebCore.Test/IHtmlNodeExtensions.cs @@ -0,0 +1,24 @@ +using System.Text.RegularExpressions; +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test +{ + internal static class IHtmlNodeExtensions + { + /// + /// Removes extra spaces and line breaks from the input node. + /// + /// The node to clean. + /// A string with all consecutive spaces and line breaks reduced to single ones. + public static string Trim(this IHtmlNode node) + { + var withoutExtraLineBreaks = Regex.Replace(node.ToString().Trim(), @"[\r\n]+", ""); + var withoutExtraSpaces = Regex.Replace(withoutExtraLineBreaks, @"\s+", " "); + var withoutSpacesBetweenBrackets = Regex.Replace(withoutExtraSpaces, @">\s+<", "><"); + var withoutSpacesLeftBrackets = Regex.Replace(withoutSpacesBetweenBrackets, @"\s+<", "<"); + var withoutSpacesRightBrackets = Regex.Replace(withoutSpacesLeftBrackets, @">\s+", ">"); + + return withoutSpacesRightBrackets; + } + } +} diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index 0e8542a..fc0342e 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -3,7 +3,7 @@ net8.0 enable - enable + disable false true diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs index 914718c..2f1a066 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs @@ -94,7 +94,7 @@ public string OnClick /// /// Determines whether the element needs an end tag. - /// e.g.: true =
false =
+ /// e.g.: true =
false =
///
public bool CloseTag { get; protected set; } From a07d5aff0e4e2ff753ac47ff872de59a5fe8fd0b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 10 Jul 2024 21:23:52 +0200 Subject: [PATCH 006/162] quality improvements --- src/WebExpress.WebCore/HttpServer.cs | 38 ++++++++-------- .../WebApplication/Application.cs | 43 +++++++++++++++++++ .../WebApplication/IApplication.cs | 9 +++- src/WebExpress.WebCore/WebModule/IModule.cs | 9 +++- src/WebExpress.WebCore/WebModule/Module.cs | 36 ++++++++++++++++ .../WebPage/RenderContext.cs | 19 ++++---- .../WebResource/IResource.cs | 17 -------- .../WebResource/Resource.cs | 14 +++--- .../WebResource/ResourceManager.cs | 2 +- .../WebSitemap/SitemapManager.cs | 12 +++--- .../WebStatusPage/IStatusPage.cs | 18 ++------ 11 files changed, 137 insertions(+), 80 deletions(-) create mode 100644 src/WebExpress.WebCore/WebApplication/Application.cs create mode 100644 src/WebExpress.WebCore/WebModule/Module.cs diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index a9d881c..c5e26d3 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -17,12 +17,10 @@ using System.Threading.Tasks; using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebSitemap; @@ -495,24 +493,24 @@ private async Task SendResponseAsync(HttpContext context, Response response) if (statusPage is Resource resource) { - resource.ApplicationContext = searchResult?.ApplicationContext ?? new ApplicationContext() - { - PluginContext = searchResult?.ModuleContext?.PluginContext ?? - searchResult?.ApplicationContext?.PluginContext, - ApplicationId = "webex", - ApplicationName = "WebExpress", - ContextPath = new UriResource() - }; - - resource.ModuleContext = searchResult?.ModuleContext ?? new ModuleContext() - { - ApplicationContext = resource.ApplicationContext, - PluginContext = searchResult?.ModuleContext?.PluginContext ?? - searchResult?.ApplicationContext?.PluginContext, - ModuleId = "webex", - ModuleName = "WebExpress", - ContextPath = new UriResource() - }; + //resource.ApplicationContext = searchResult?.ApplicationContext ?? new ApplicationContext() + //{ + // PluginContext = searchResult?.ModuleContext?.PluginContext ?? + // searchResult?.ApplicationContext?.PluginContext, + // ApplicationId = "webex", + // ApplicationName = "WebExpress", + // ContextPath = new UriResource() + //}; + + //resource.ModuleContext = searchResult?.ModuleContext ?? new ModuleContext() + //{ + // ApplicationContext = resource.ApplicationContext, + // PluginContext = searchResult?.ModuleContext?.PluginContext ?? + // searchResult?.ApplicationContext?.PluginContext, + // ModuleId = "webex", + // ModuleName = "WebExpress", + // ContextPath = new UriResource() + //}; resource.Initialization(new ResourceContext(resource.ModuleContext)); } diff --git a/src/WebExpress.WebCore/WebApplication/Application.cs b/src/WebExpress.WebCore/WebApplication/Application.cs new file mode 100644 index 0000000..748aca5 --- /dev/null +++ b/src/WebExpress.WebCore/WebApplication/Application.cs @@ -0,0 +1,43 @@ +namespace WebExpress.WebCore.WebApplication +{ + /// + /// This represents an application. + /// + public abstract class Application : IApplication + { + /// + /// Returns the context of the application. + /// + public IApplicationContext ApplicationContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Application() + { + } + + /// + /// Initialization of the application. Here, for example, managed resources can be loaded. + /// + /// The context that applies to the execution of the application + public virtual void Initialization(IApplicationContext applicationContext) + { + ApplicationContext = applicationContext; + } + + /// + /// Called when the application starts working. The call is concurrent. + /// + public virtual void Run() + { + } + + /// + /// Release unmanaged resources that have been reserved during use. + /// + public virtual void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebApplication/IApplication.cs b/src/WebExpress.WebCore/WebApplication/IApplication.cs index 18e2fd2..801538b 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplication.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplication.cs @@ -7,11 +7,16 @@ namespace WebExpress.WebCore.WebApplication ///
public interface IApplication : IDisposable { + /// + /// Returns the context of the application. + /// + IApplicationContext ApplicationContext { get; } + /// /// Initialization of the application . /// - /// The context. - void Initialization(IApplicationContext context); + /// The context. + void Initialization(IApplicationContext applicationContext); /// /// Called when the application starts working. The call is concurrent. diff --git a/src/WebExpress.WebCore/WebModule/IModule.cs b/src/WebExpress.WebCore/WebModule/IModule.cs index ca30186..c917775 100644 --- a/src/WebExpress.WebCore/WebModule/IModule.cs +++ b/src/WebExpress.WebCore/WebModule/IModule.cs @@ -4,11 +4,16 @@ namespace WebExpress.WebCore.WebModule { public interface IModule : IDisposable { + /// + /// Returns the context of the module. + /// + IModuleContext ModuleContext { get; } + /// /// Initialization of the module. /// - /// The context. - void Initialization(IModuleContext context); + /// The context. + void Initialization(IModuleContext moduleContext); /// /// Called when the module starts working. The call is concurrent. diff --git a/src/WebExpress.WebCore/WebModule/Module.cs b/src/WebExpress.WebCore/WebModule/Module.cs new file mode 100644 index 0000000..c9f1dce --- /dev/null +++ b/src/WebExpress.WebCore/WebModule/Module.cs @@ -0,0 +1,36 @@ +namespace WebExpress.WebCore.WebModule +{ + /// + /// This represents an module. + /// + public abstract class Module : IModule + { + /// + /// Returns the context of the module. + /// + public IModuleContext ModuleContext { get; private set; } + + /// + /// Initialization of the module. + /// + /// The context. + public virtual void Initialization(IModuleContext moduleContext) + { + ModuleContext = moduleContext; + } + + /// + /// Called when the application starts working. The call is concurrent. + /// + public virtual void Run() + { + } + + /// + /// Release unmanaged resources that have been reserved during use. + /// + public virtual void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 0fe00a2..6dd4111 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,7 +1,6 @@ using System.Globalization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; @@ -29,10 +28,10 @@ public class RenderContext : II18N /// public UriResource Uri => Request.Uri; - /// - /// Returns the context path. - /// - public UriResource ContextPath => Page?.ResourceContext?.ContextPath; + ///// + ///// Returns the context path. + ///// + //public UriResource ContextPath => Page?.ResourceContext?.ContextPath; /// /// Returns the culture. @@ -46,17 +45,17 @@ public CultureInfo Culture /// /// Provides the context of the associated application. /// - public IApplicationContext ApplicationContext => Page?.ApplicationContext; + public IApplicationContext ApplicationContext => Page?.ResourceContext.ApplicationContext; /// /// Returns the contents of a page. /// public IVisualTree VisualTree { get; protected set; } - /// - /// Returns the log for writing status messages to the console and to a log file. - /// - public Log Log { get; private set; } + ///// + ///// Returns the log for writing status messages to the console and to a log file. + ///// + //public Log Log { get; private set; } /// /// Constructor diff --git a/src/WebExpress.WebCore/WebResource/IResource.cs b/src/WebExpress.WebCore/WebResource/IResource.cs index ebf21b3..50d209f 100644 --- a/src/WebExpress.WebCore/WebResource/IResource.cs +++ b/src/WebExpress.WebCore/WebResource/IResource.cs @@ -1,27 +1,10 @@ using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; namespace WebExpress.WebCore.WebResource { public interface IResource : II18N { - /// - /// Returns the resource Id. - /// - string Id { get; } - - /// - /// Returns the context of the application. - /// - IApplicationContext ApplicationContext { get; } - - /// - /// Returns the context of the module. - /// - IModuleContext ModuleContext { get; } - /// /// Returns the module context where the resource exists. /// diff --git a/src/WebExpress.WebCore/WebResource/Resource.cs b/src/WebExpress.WebCore/WebResource/Resource.cs index 87e7a6f..1e4a81c 100644 --- a/src/WebExpress.WebCore/WebResource/Resource.cs +++ b/src/WebExpress.WebCore/WebResource/Resource.cs @@ -7,23 +7,23 @@ namespace WebExpress.WebCore.WebResource { public abstract class Resource : IResource { - /// - /// Returns the resource id. - /// - public string Id { get; internal set; } + ///// + ///// Returns the resource id. + ///// + //public string Id { get; internal set; } /// /// Returns the context of the application. /// - public IApplicationContext ApplicationContext { get; internal set; } + public IApplicationContext ApplicationContext => ResourceContext?.ApplicationContext; /// /// Returns the context of the module. /// - public IModuleContext ModuleContext { get; internal set; } + public IModuleContext ModuleContext => ResourceContext?.ModuleContext; /// - /// Returns the module context where the resource exists. + /// Returns the resource context where the resource exists. /// public IResourceContext ResourceContext { get; private set; } diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index affa6c1..a310efd 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -46,7 +46,7 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent /// /// Returns all resource contexts. /// - internal IEnumerable Resources => Dictionary.Values + public IEnumerable Resources => Dictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x.ResourceContexts); diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 937c211..d931234 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -443,12 +443,12 @@ private static IResource CreateInstance(SitemapNode node, UriResource uri, Searc i18n.Culture = context.Culture; } - if (instance is Resource resorce) - { - resorce.Id = node.ResourceItem?.ResourceId; - resorce.ApplicationContext = node.ResourceContext?.ModuleContext?.ApplicationContext; - resorce.ModuleContext = node.ResourceContext?.ModuleContext; - } + //if (instance is Resource resorce) + //{ + //resorce.Id = node.ResourceItem?.ResourceId; + //resorce.ApplicationContext = node.ResourceContext?.ModuleContext?.ApplicationContext; + //resorce.ModuleContext = node.ResourceContext?.ModuleContext; + //} if (instance is IPage page) { diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs index 5b5b7ab..4aff730 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs @@ -1,6 +1,4 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; @@ -12,19 +10,9 @@ namespace WebExpress.WebCore.WebStatusPage public interface IStatusPage { /// - /// Returns the resource Id. + /// Returns the resource context where the resource exists. /// - string Id { get; } - - /// - /// Returns the context of the application. - /// - IApplicationContext ApplicationContext { get; } - - /// - /// Returns the context of the module. - /// - IModuleContext ModuleContext { get; } + IResourceContext ResourceContext { get; } /// /// Returns or sets the status code. From 10e3ec1f745adf4e18a90c285171759f2adfedfc Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Thu, 11 Jul 2024 21:57:29 +0200 Subject: [PATCH 007/162] quality improvements --- src/WebExpress.WebCore/WebPlugin/IPlugin.cs | 9 ++++- src/WebExpress.WebCore/WebPlugin/Plugin.cs | 43 +++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 src/WebExpress.WebCore/WebPlugin/Plugin.cs diff --git a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs index f1795c6..f46cef0 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs @@ -7,11 +7,16 @@ namespace WebExpress.WebCore.WebPlugin /// public interface IPlugin : IDisposable { + /// + /// Returns the context of the plugin. + /// + IPluginContext PluginContext { get; } + /// /// Initialization of the plugin. /// - /// The context. - void Initialization(IPluginContext context); + /// The context. + void Initialization(IPluginContext pluginContext); /// /// Called when the plugin starts working. The call is concurrent. diff --git a/src/WebExpress.WebCore/WebPlugin/Plugin.cs b/src/WebExpress.WebCore/WebPlugin/Plugin.cs new file mode 100644 index 0000000..677991b --- /dev/null +++ b/src/WebExpress.WebCore/WebPlugin/Plugin.cs @@ -0,0 +1,43 @@ +namespace WebExpress.WebCore.WebPlugin +{ + /// + /// This represents an plugin. + /// + public abstract class Plugin : IPlugin + { + /// + /// Returns the context of the plugin. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public Plugin() + { + } + + /// + /// Initialization of the application. Here, for example, managed resources can be loaded. + /// + /// The context that applies to the execution of the application + public virtual void Initialization(IPluginContext pluginContext) + { + PluginContext = pluginContext; + } + + /// + /// Called when the application starts working. The call is concurrent. + /// + public virtual void Run() + { + } + + /// + /// Release unmanaged resources that have been reserved during use. + /// + public virtual void Dispose() + { + } + } +} From 9f460fa95f5f9b38a7fa9344a256bf3290e3ce2f Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 11 Aug 2024 21:33:28 +0200 Subject: [PATCH 008/162] refactoring --- src/WebExpress.WebCore/ArgumentParser.cs | 2 +- .../Config/EndpointConfig.cs | 2 +- .../Config/HttpServerConfig.cs | 2 +- src/WebExpress.WebCore/Config/LimitConfig.cs | 2 +- src/WebExpress.WebCore/Config/PluginConfig.cs | 2 +- src/WebExpress.WebCore/HttpServer.cs | 4 +- src/WebExpress.WebCore/HttpServerContext.cs | 2 +- .../InternationalizationExtensions.cs | 36 +++++++++ .../InternationalizationManager.cs | 2 +- .../Internationalization/de | 10 +-- .../Internationalization/en | 10 +-- .../Setting/SettingLogItem.cs | 2 +- .../WebApplication/ApplicationContext.cs | 2 +- .../WebApplication/ApplicationManager.cs | 2 +- .../WebApplication/IApplication.cs | 9 +-- .../WebAttribute/ApplicationAttribute.cs | 2 +- .../WebAttribute/AssetPathAttribute.cs | 2 +- .../WebAttribute/CacheAttribute.cs | 2 +- .../WebAttribute/ConditionAttribute.cs | 2 +- .../WebAttribute/ContextPathAttribute.cs | 2 +- .../WebAttribute/DataPathAttribute.cs | 2 +- .../WebAttribute/DefaultAttribute.cs | 2 +- .../WebAttribute/DependencyAttribute.cs | 2 +- .../WebAttribute/DescriptionAttribute.cs | 2 +- .../WebAttribute/IconAttribute.cs | 2 +- .../WebAttribute/IncludeSubPathsAttribute.cs | 2 +- .../WebAttribute/JobAttribute.cs | 2 +- .../WebAttribute/ModuleAttribute.cs | 2 +- .../WebAttribute/NameAttribute.cs | 2 +- .../WebAttribute/OptionAttribute.cs | 2 +- .../WebAttribute/OptionalAttribute.cs | 2 +- .../WebAttribute/ParentAttribute.cs | 2 +- .../WebAttribute/ScopeAttribute.cs | 2 +- .../WebAttribute/SegmentAttribute.cs | 2 +- .../WebAttribute/SegmentDoubleAttribute.cs | 2 +- .../WebAttribute/SegmentGuidAttribute.cs | 2 +- .../WebAttribute/SegmentIntAttribute.cs | 2 +- .../WebAttribute/SegmentStringAttribute.cs | 2 +- .../WebAttribute/SegmentUIntAttribute.cs | 2 +- .../WebAttribute/StatusCodeAttribute.cs | 2 +- .../WebAttribute/SystemPluginAttribute.cs | 2 +- .../WebAttribute/TitleAttribute.cs | 2 +- .../WebComponent/ComponentItem.cs | 2 +- .../WebComponent/ComponentManager.cs | 12 +-- .../WebEvent/EventManager.cs | 2 +- src/WebExpress.WebCore/WebHtml/Favicon.cs | 6 +- .../WebHtml/HtmlAttribute.cs | 6 +- .../WebHtml/HtmlAttributeNoneValue.cs | 4 +- src/WebExpress.WebCore/WebHtml/HtmlComment.cs | 4 +- src/WebExpress.WebCore/WebHtml/HtmlElement.cs | 4 +- .../WebHtml/HtmlElementEditDel.cs | 6 +- .../WebHtml/HtmlElementEditIns.cs | 6 +- .../WebHtml/HtmlElementEmbeddedEmbed.cs | 6 +- .../WebHtml/HtmlElementEmbeddedIframe.cs | 6 +- .../WebHtml/HtmlElementEmbeddedObject.cs | 6 +- .../WebHtml/HtmlElementEmbeddedParam.cs | 2 +- .../WebHtml/HtmlElementEmbeddedPicture.cs | 6 +- .../WebHtml/HtmlElementEmbeddedSource.cs | 2 +- .../WebHtml/HtmlElementFieldButton.cs | 10 +-- .../WebHtml/HtmlElementFieldInput.cs | 6 +- .../WebHtml/HtmlElementFieldLabel.cs | 8 +- .../WebHtml/HtmlElementFieldLegend.cs | 4 +- .../WebHtml/HtmlElementFieldSelect.cs | 6 +- .../WebHtml/HtmlElementFormDatalist.cs | 6 +- .../WebHtml/HtmlElementFormFieldset.cs | 6 +- .../WebHtml/HtmlElementFormForm.cs | 6 +- .../WebHtml/HtmlElementFormKeygen.cs | 6 +- .../WebHtml/HtmlElementFormMeter.cs | 4 +- .../WebHtml/HtmlElementFormOptgroup.cs | 6 +- .../WebHtml/HtmlElementFormOption.cs | 6 +- .../WebHtml/HtmlElementFormOutput.cs | 6 +- .../WebHtml/HtmlElementFormProgress.cs | 4 +- .../WebHtml/HtmlElementFormTextarea.cs | 6 +- .../WebHtml/HtmlElementInteractiveCommand.cs | 6 +- .../WebHtml/HtmlElementInteractiveDetails.cs | 6 +- .../WebHtml/HtmlElementInteractiveMenu.cs | 6 +- .../WebHtml/HtmlElementInteractiveSummary.cs | 6 +- .../WebHtml/HtmlElementMetadataBase.cs | 4 +- .../WebHtml/HtmlElementMetadataHead.cs | 2 +- .../WebHtml/HtmlElementMetadataLink.cs | 2 +- .../WebHtml/HtmlElementMetadataMeta.cs | 6 +- .../WebHtml/HtmlElementMetadataStyle.cs | 4 +- .../WebHtml/HtmlElementMetadataTitle.cs | 4 +- .../WebHtml/HtmlElementMultimediaArea.cs | 2 +- .../WebHtml/HtmlElementMultimediaAudio.cs | 2 +- .../WebHtml/HtmlElementMultimediaImg.cs | 2 +- .../WebHtml/HtmlElementMultimediaMap.cs | 2 +- .../WebHtml/HtmlElementMultimediaMath.cs | 6 +- .../WebHtml/HtmlElementMultimediaSvg.cs | 6 +- .../WebHtml/HtmlElementMultimediaTrack.cs | 2 +- .../WebHtml/HtmlElementMultimediaVideo.cs | 2 +- .../WebHtml/HtmlElementRootHtml.cs | 2 +- .../WebHtml/HtmlElementScriptingCanvas.cs | 2 +- .../WebHtml/HtmlElementScriptingNoscript.cs | 6 +- .../WebHtml/HtmlElementScriptingScript.cs | 4 +- .../WebHtml/HtmlElementSectionAddress.cs | 6 +- .../WebHtml/HtmlElementSectionArticle.cs | 6 +- .../WebHtml/HtmlElementSectionAside.cs | 6 +- .../WebHtml/HtmlElementSectionBody.cs | 4 +- .../WebHtml/HtmlElementSectionFooter.cs | 8 +- .../WebHtml/HtmlElementSectionH1.cs | 6 +- .../WebHtml/HtmlElementSectionH2.cs | 6 +- .../WebHtml/HtmlElementSectionH3.cs | 6 +- .../WebHtml/HtmlElementSectionH4.cs | 6 +- .../WebHtml/HtmlElementSectionH5.cs | 6 +- .../WebHtml/HtmlElementSectionH6.cs | 6 +- .../WebHtml/HtmlElementSectionHeader.cs | 6 +- .../WebHtml/HtmlElementSectionMain.cs | 6 +- .../WebHtml/HtmlElementSectionNav.cs | 6 +- .../WebHtml/HtmlElementSectionSection.cs | 6 +- .../WebHtml/HtmlElementTableCaption.cs | 6 +- .../WebHtml/HtmlElementTableCol.cs | 2 +- .../WebHtml/HtmlElementTableColgroup.cs | 2 +- .../WebHtml/HtmlElementTableTable.cs | 2 +- .../WebHtml/HtmlElementTableTbody.cs | 6 +- .../WebHtml/HtmlElementTableTd.cs | 8 +- .../WebHtml/HtmlElementTableTfoot.cs | 6 +- .../WebHtml/HtmlElementTableTh.cs | 6 +- .../WebHtml/HtmlElementTableThead.cs | 6 +- .../WebHtml/HtmlElementTableTr.cs | 6 +- .../HtmlElementTextContentBlockquote.cs | 6 +- .../WebHtml/HtmlElementTextContentDd.cs | 6 +- .../WebHtml/HtmlElementTextContentDiv.cs | 6 +- .../WebHtml/HtmlElementTextContentDl.cs | 6 +- .../WebHtml/HtmlElementTextContentDt.cs | 6 +- .../HtmlElementTextContentFigcaption.cs | 6 +- .../WebHtml/HtmlElementTextContentFigure.cs | 6 +- .../WebHtml/HtmlElementTextContentHr.cs | 2 +- .../WebHtml/HtmlElementTextContentLi.cs | 6 +- .../WebHtml/HtmlElementTextContentOl.cs | 4 +- .../WebHtml/HtmlElementTextContentP.cs | 6 +- .../WebHtml/HtmlElementTextContentPre.cs | 6 +- .../WebHtml/HtmlElementTextContentUl.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsA.cs | 8 +- .../WebHtml/HtmlElementTextSemanticsAbbr.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsB.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsBdi.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsBdo.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsBr.cs | 2 +- .../WebHtml/HtmlElementTextSemanticsCite.cs | 8 +- .../WebHtml/HtmlElementTextSemanticsCode.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsData.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsDfn.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsEm.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsI.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsKdb.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsMark.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsQ.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsRp.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsRt.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsRuby.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsS.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsSamp.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsSmall.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsSpan.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsStrong.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsSub.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsSup.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsTime.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsU.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsVar.cs | 6 +- .../WebHtml/HtmlElementTextSemanticsWbr.cs | 2 +- .../WebHtml/HtmlElementWebComponentsSlot.cs | 6 +- .../HtmlElementWebComponentsTemplate.cs | 6 +- src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs | 2 +- src/WebExpress.WebCore/WebHtml/HtmlList.cs | 10 +-- src/WebExpress.WebCore/WebHtml/HtmlNbsp.cs | 2 +- src/WebExpress.WebCore/WebHtml/HtmlRaw.cs | 4 +- src/WebExpress.WebCore/WebHtml/HtmlText.cs | 4 +- .../WebHtml/IHtmlElementFormItem.cs | 6 ++ .../WebHtml/IHtmlFormularItem.cs | 6 -- src/WebExpress.WebCore/WebJob/Clock.cs | 4 +- src/WebExpress.WebCore/WebJob/Cron.cs | 2 +- src/WebExpress.WebCore/WebJob/JobContext.cs | 4 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 2 +- src/WebExpress.WebCore/WebLog/Log.cs | 2 +- src/WebExpress.WebCore/WebLog/LogFrame.cs | 4 +- .../WebLog/LogFrameSimple.cs | 2 +- src/WebExpress.WebCore/WebLog/LogItem.cs | 2 +- .../WebMessage/HttpContext.cs | 4 +- .../WebMessage/HttpExceptionContext.cs | 2 +- .../WebMessage/Parameter.cs | 10 +-- .../WebMessage/ParameterFile.cs | 8 +- src/WebExpress.WebCore/WebMessage/Request.cs | 2 +- .../WebMessage/RequestHeaderFields.cs | 2 +- src/WebExpress.WebCore/WebMessage/Response.cs | 2 +- .../WebMessage/ResponseBadRequest.cs | 2 +- .../WebMessage/ResponseForbidden.cs | 2 +- .../WebMessage/ResponseHeaderFields.cs | 2 +- .../WebMessage/ResponseInternalServerError.cs | 2 +- .../WebMessage/ResponseNotFound.cs | 2 +- .../WebMessage/ResponseOK.cs | 2 +- .../ResponseRedirectPermanentlyMoved.cs | 2 +- .../ResponseRedirectTemporarilyMoved.cs | 2 +- .../WebMessage/ResponseUnauthorized.cs | 2 +- src/WebExpress.WebCore/WebModule/IModule.cs | 9 +-- .../WebModule/ModuleContext.cs | 4 +- .../WebModule/ModuleManager.cs | 2 +- .../WebPackage/PackageItem.cs | 2 +- .../WebPackage/PackageManager.cs | 2 +- src/WebExpress.WebCore/WebPage/IPage.cs | 5 ++ src/WebExpress.WebCore/WebPage/Page.cs | 2 +- .../WebPage/RenderContext.cs | 41 ++++------ src/WebExpress.WebCore/WebPage/VisualTree.cs | 2 +- src/WebExpress.WebCore/WebPlugin/IPlugin.cs | 9 +-- src/WebExpress.WebCore/WebPlugin/Plugin.cs | 2 +- .../WebPlugin/PluginContext.cs | 2 +- .../WebPlugin/PluginLoadContext.cs | 2 +- .../WebPlugin/PluginManager.cs | 8 +- .../WebResource/IResource.cs | 7 +- .../WebResource/RedirectException.cs | 2 +- .../WebResource/Resource.cs | 2 +- .../WebResource/ResourceAsset.cs | 2 +- .../WebResource/ResourceBinary.cs | 2 +- .../WebResource/ResourceContext.cs | 2 +- .../WebResource/ResourceFile.cs | 2 +- .../WebResource/ResourceManager.cs | 2 +- .../WebResource/ResourceRest.cs | 2 +- src/WebExpress.WebCore/WebSession/Session.cs | 4 +- .../WebSession/SessionManager.cs | 2 +- .../WebSession/SessionPropertyParameter.cs | 4 +- .../WebSitemap/SearchResult.cs | 2 +- .../WebSitemap/SitemapManager.cs | 2 +- .../WebSitemap/SitemapNode.cs | 2 +- .../WebStatusPage/IStatusPageContext.cs | 28 +++++++ .../WebStatusPage/StatusPageContext.cs | 28 +++++++ ...eDictionary.cs => StatusPageDictionary.cs} | 2 +- ...aryItem.cs => StatusPageDictionaryItem.cs} | 2 +- .../{ResponseItem.cs => StatusPageItem.cs} | 21 +++-- ...esponseManager.cs => StatusPageManager.cs} | 77 +++++++++++++------ src/WebExpress.WebCore/WebTask/TaskManager.cs | 2 +- src/WebExpress.WebCore/WebUri/UriAuthority.cs | 6 +- src/WebExpress.WebCore/WebUri/UriFragment.cs | 2 +- .../WebUri/UriPathSegmentConstant.cs | 4 +- .../WebUri/UriPathSegmentRoot.cs | 2 +- .../WebUri/UriPathSegmentVariable.cs | 4 +- .../WebUri/UriPathSegmentVariableDouble.cs | 6 +- .../WebUri/UriPathSegmentVariableGuid.cs | 8 +- .../WebUri/UriPathSegmentVariableInt.cs | 6 +- .../WebUri/UriPathSegmentVariableString.cs | 6 +- .../WebUri/UriPathSegmentVariableUInt.cs | 6 +- src/WebExpress.WebCore/WebUri/UriQuerry.cs | 2 +- src/WebExpress.WebCore/WebUri/UriResource.cs | 32 ++++---- 243 files changed, 683 insertions(+), 585 deletions(-) create mode 100644 src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs delete mode 100644 src/WebExpress.WebCore/WebHtml/IHtmlFormularItem.cs create mode 100644 src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs create mode 100644 src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs rename src/WebExpress.WebCore/WebStatusPage/{ResponseDictionary.cs => StatusPageDictionary.cs} (71%) rename src/WebExpress.WebCore/WebStatusPage/{ResponseDictionaryItem.cs => StatusPageDictionaryItem.cs} (71%) rename src/WebExpress.WebCore/WebStatusPage/{ResponseItem.cs => StatusPageItem.cs} (62%) rename src/WebExpress.WebCore/WebStatusPage/{ResponseManager.cs => StatusPageManager.cs} (79%) diff --git a/src/WebExpress.WebCore/ArgumentParser.cs b/src/WebExpress.WebCore/ArgumentParser.cs index 37f4412..87cbf83 100644 --- a/src/WebExpress.WebCore/ArgumentParser.cs +++ b/src/WebExpress.WebCore/ArgumentParser.cs @@ -36,7 +36,7 @@ public static ArgumentParser Current } /// - /// Constructor + /// Initializes a new instance of the class. /// public ArgumentParser() { diff --git a/src/WebExpress.WebCore/Config/EndpointConfig.cs b/src/WebExpress.WebCore/Config/EndpointConfig.cs index 4f89d9f..d3421b7 100644 --- a/src/WebExpress.WebCore/Config/EndpointConfig.cs +++ b/src/WebExpress.WebCore/Config/EndpointConfig.cs @@ -27,7 +27,7 @@ public sealed class EndpointConfig public string Password { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public EndpointConfig() { diff --git a/src/WebExpress.WebCore/Config/HttpServerConfig.cs b/src/WebExpress.WebCore/Config/HttpServerConfig.cs index adfa1da..18cd34e 100644 --- a/src/WebExpress.WebCore/Config/HttpServerConfig.cs +++ b/src/WebExpress.WebCore/Config/HttpServerConfig.cs @@ -71,7 +71,7 @@ public sealed class HttpServerConfig public SettingLogItem Log { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HttpServerConfig() { diff --git a/src/WebExpress.WebCore/Config/LimitConfig.cs b/src/WebExpress.WebCore/Config/LimitConfig.cs index e3e41a0..284ae85 100644 --- a/src/WebExpress.WebCore/Config/LimitConfig.cs +++ b/src/WebExpress.WebCore/Config/LimitConfig.cs @@ -21,7 +21,7 @@ public sealed class LimitConfig public long UploadLimit { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public LimitConfig() { diff --git a/src/WebExpress.WebCore/Config/PluginConfig.cs b/src/WebExpress.WebCore/Config/PluginConfig.cs index 8c1ebe2..2ecd89a 100644 --- a/src/WebExpress.WebCore/Config/PluginConfig.cs +++ b/src/WebExpress.WebCore/Config/PluginConfig.cs @@ -21,7 +21,7 @@ public sealed class PluginConfig public string ContextPath { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public PluginConfig() { diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index c5e26d3..6a3719a 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -74,7 +74,7 @@ public class HttpServer : IHost, II18N, IHttpApplication public long RequestNumber { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// Der Serverkontext. public HttpServer(HttpServerContext context) @@ -473,7 +473,7 @@ private async Task SendResponseAsync(HttpContext context, Response response) if (searchResult != null) { - var statusPage = ComponentManager.ResponseManager.CreateStatusPage + var statusPage = ComponentManager.StatusPageManager.CreateStatusPage ( massage, response.Status, diff --git a/src/WebExpress.WebCore/HttpServerContext.cs b/src/WebExpress.WebCore/HttpServerContext.cs index c822290..708ab1e 100644 --- a/src/WebExpress.WebCore/HttpServerContext.cs +++ b/src/WebExpress.WebCore/HttpServerContext.cs @@ -68,7 +68,7 @@ public class HttpServerContext : IHttpServerContext public IHost Host { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri of the web server. /// The endpoints to which the web server responds. diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 6c02023..3ded8b6 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPage; namespace WebExpress.WebCore.Internationalization { @@ -38,5 +39,40 @@ public static string I18N(this II18N obj, IApplicationContext applicationContext { return InternationalizationManager.I18N(obj.Culture, applicationContext?.PluginContext?.PluginId, key); } + + /// + /// Internationalization of a key. + /// + /// An render context object that is being extended. + /// The plugin id. + /// The internationalization key. + /// The value of the key in the current language. + public static string I18N(this RenderContext obj, string pluginId, string key) + { + return InternationalizationManager.I18N(obj.Culture, pluginId, key); + } + + /// + /// Internationalization of a key. + /// + /// An render context object that is being extended. + /// The internationalization key. + /// The value of the key in the current language. + public static string I18N(this RenderContext obj, string key) + { + return InternationalizationManager.I18N(obj.Culture, obj?.PluginContext?.PluginId, key); + } + + /// + /// Internationalization of a key. + /// + /// An render context object that is being extended. + /// The allication context. + /// The internationalization key. + /// The value of the key in the current language. + public static string I18N(this RenderContext obj, IApplicationContext applicationContext, string key) + { + return InternationalizationManager.I18N(obj.Culture, applicationContext?.PluginContext?.PluginId, key); + } } } diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 13d8a76..66203cd 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -30,7 +30,7 @@ public sealed class InternationalizationManager : IComponentPlugin, ISystemCompo public IHttpServerContext HttpServerContext { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// internal InternationalizationManager() { diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 5dc61b0..526b63b 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -100,11 +100,11 @@ resourcemanager.sitemap=Inhalt der Sitemap für das Modul '{0}': resourcemanager.wrongtype=Der Type '{0}' implementiert die Schnittstelle '{1}' nicht. resourcemanager.resource=Ressource: '{0}' für das Modul '{1}' -responsemanager.initialization=Der Responsemanagermanager wurde initialisiert. -responsemanager.register=Status {0} wurde im Module '{1}' registriert und der Statusseite '{2}' zugewiesen. -responsemanager.duplicat=Der Status {0} wurde bereits im Module '{1}' registriert registriert. Die Statusseite '{2}' wird daher nicht verwendet. -responsemanager.statuscode=Ein Statuscode wurde der Ressource '{1}' für das Modul '{0}' nicht zugewiesen. -responsemanager.statuspage=Statuscode: '{0}' +statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. +statuspagemanager.register=Status {0} wurde registriert und der Statusseite '{1}' zugewiesen. +statuspagemanager.duplicat=Der Status {0} wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. +statuspagemanager.statuscode=Ein Statuscode wurde der Ressource '{1}' für das Modul '{0}' nicht zugewiesen. +statuspagemanager.statuspage=Statuscode: '{0}' sitemapmanager.initialization=Der Sitemap-Manager wurde initialisiert. sitemapmanager.refresh=Die Sitemap wird neu aufgebaut. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 73c4def..a46a697 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -100,11 +100,11 @@ resourcemanager.sitemap=Sitemap content resource '{0}' application: resourcemanager.wrongtype=The type '{0}' does not implement the interface '{1}'. resourcemanager.statuspage=Resource: '{0}' for module '{1}' -responsemanager.initialization=The response manager has been initialized. -responsemanager.register=Status {0} has been registered in the module '{1}' and assigned to the status page '{2}'. -responsemanager.duplicat=The status {0} has already been registered in the module '{1}'. Therefore, the status page '{2}' is not used. -responsemanager.statuscode=A status code has not been assigned to the resource '{1}' for the module '{0}'. -responsemanager.resource=Status code: '{0}' +statuspagemanager.initialization=The status page manager has been initialized. +statuspagemanager.register=Status {0} has been registered and assigned to the status page '{1}'. +statuspagemanager.duplicat=The status {0} has already been registered. Therefore, the status page '{1}' is not used. +statuspagemanager.statuscode=A status code has not been assigned to the resource '{1}' for the module '{0}'. +statuspagemanager.resource=Status code: '{0}' sitemapmanager.initialization=The sitemap manager has been initialized. sitemapmanager.refresh=The sitemap will be rebuilt. diff --git a/src/WebExpress.WebCore/Setting/SettingLogItem.cs b/src/WebExpress.WebCore/Setting/SettingLogItem.cs index e72a49a..88c66ff 100644 --- a/src/WebExpress.WebCore/Setting/SettingLogItem.cs +++ b/src/WebExpress.WebCore/Setting/SettingLogItem.cs @@ -45,7 +45,7 @@ public class SettingLogItem : ISettingItem public string Timepattern { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public SettingLogItem() { diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs index 9d58cda..36324c5 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs @@ -52,7 +52,7 @@ public class ApplicationContext : IApplicationContext public UriResource Icon { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public ApplicationContext() { diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index ff23a8a..4d4f706 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -43,7 +43,7 @@ public sealed class ApplicationManager : IComponentPlugin, IExecutableElements, public IEnumerable Applications => Dictionary.Values.SelectMany(x => x.Values).Select(x => x.ApplicationContext); /// - /// Constructor + /// Initializes a new instance of the class. /// internal ApplicationManager() { diff --git a/src/WebExpress.WebCore/WebApplication/IApplication.cs b/src/WebExpress.WebCore/WebApplication/IApplication.cs index 801538b..4c7ffd4 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplication.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplication.cs @@ -8,14 +8,9 @@ namespace WebExpress.WebCore.WebApplication public interface IApplication : IDisposable { /// - /// Returns the context of the application. + /// Initialization of the application. Here, for example, managed resources can be loaded. /// - IApplicationContext ApplicationContext { get; } - - /// - /// Initialization of the application . - /// - /// The context. + /// The application context. void Initialization(IApplicationContext applicationContext); /// diff --git a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs index 836a363..36e471b 100644 --- a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs @@ -10,7 +10,7 @@ namespace WebExpress.WebCore.WebAttribute public class ApplicationAttribute : Attribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// A specific ApplicationId, regular expression, or * for any application. public ApplicationAttribute(string applicationId) diff --git a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs index b535463..78d68a5 100644 --- a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs @@ -3,7 +3,7 @@ public class AssetPathAttribute : System.Attribute, IApplicationAttribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path for assets. public AssetPathAttribute(string assetPath) diff --git a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs index 968e73c..96348b2 100644 --- a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebAttribute public class CacheAttribute : System.Attribute, IResourceAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// public CacheAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs index 4b32bb1..27fa603 100644 --- a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs @@ -10,7 +10,7 @@ namespace WebExpress.WebCore.WebAttribute public class ConditionAttribute : Attribute, IResourceAttribute where T : class, ICondition { /// - /// Constructor + /// Initializes a new instance of the class. /// public ConditionAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs index 0fa43f5..b1e679a 100644 --- a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute public class ContextPathAttribute : Attribute, IApplicationAttribute, IModuleAttribute, IResourceAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The context path. public ContextPathAttribute(string contetxPath) diff --git a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs index 5cfe84c..ab102a5 100644 --- a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs @@ -3,7 +3,7 @@ public class DataPathAttribute : System.Attribute, IApplicationAttribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path for the data. public DataPathAttribute(string dataPath) diff --git a/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs index 7b45a50..5e67fe4 100644 --- a/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebAttribute public class DefaultAttribute : Attribute, IStatusPageAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// public DefaultAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs index d1519d9..fc7d507 100644 --- a/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebAttribute public class DependencyAttribute : System.Attribute, IPluginAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The Id of the plugin to which there is a dependency. public DependencyAttribute(string dependency) diff --git a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs index 414e31e..b727de5 100644 --- a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs @@ -3,7 +3,7 @@ public class DescriptionAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The description. public DescriptionAttribute(string description) diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 6257c6f..8b2a374 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -3,7 +3,7 @@ public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The icon. public IconAttribute(string icon) diff --git a/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs index 6e596ec..9a93620 100644 --- a/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs @@ -6,7 +6,7 @@ public class IncludeSubPathsAttribute : System.Attribute, IResourceAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// All subpaths are included. public IncludeSubPathsAttribute(bool includeSubPaths) diff --git a/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs b/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs index a1c431e..e8c8ca5 100644 --- a/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs @@ -3,7 +3,7 @@ public class JobAttribute : System.Attribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The minute 0-59 or * for any. Comma-separated values or ranges (-) are also possible. /// The hour 0-23 or * for arbitrary. Comma-separated values or ranges (-) are also possible. diff --git a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs index 6face4f..0c2da75 100644 --- a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs @@ -11,7 +11,7 @@ namespace WebExpress.WebCore.WebAttribute public class ModuleAttribute : Attribute, IResourceAttribute, IModuleAttribute where T : class, IModule { /// - /// Constructor + /// Initializes a new instance of the class. /// public ModuleAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs index 03e02a6..d13c48b 100644 --- a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs @@ -3,7 +3,7 @@ public class NameAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. public NameAttribute(string name) diff --git a/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs index 5fa300f..2a9ba99 100644 --- a/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs @@ -11,7 +11,7 @@ namespace WebExpress.WebCore.WebAttribute public class OptionAttribute : Attribute, IApplicationAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The option to activate. public OptionAttribute(string option) diff --git a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs index 792b663..d97abe4 100644 --- a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebAttribute public class OptionalAttribute : Attribute, IResourceAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// public OptionalAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs index ae0a043..30c36d9 100644 --- a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute public class ParentAttribute : Attribute, IResourceAttribute where T : class, IResource { /// - /// Constructor + /// Initializes a new instance of the class. /// public ParentAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs index fdb024e..ccdfd4b 100644 --- a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs @@ -10,7 +10,7 @@ namespace WebExpress.WebCore.WebAttribute public class ScopeAttribute : Attribute, IResourceAttribute where T : class, IScope { /// - /// Constructor + /// Initializes a new instance of the class. /// public ScopeAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs index 8c7954d..927a6aa 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs @@ -20,7 +20,7 @@ public class SegmentAttribute : Attribute, IResourceAttribute, ISegmentAttribute private string Display { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The segment of the uri path. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs index 96d700f..63bcab3 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs @@ -16,7 +16,7 @@ public class SegmentDoubleAttribute : Attribute, IResourceAttribute, ISegmentAtt private string Display { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the variable. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs index 47538bd..8a2a234 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs @@ -26,7 +26,7 @@ public class SegmentGuidAttribute : Attribute, IResourceAttribute, ISegmentAt private UriPathSegmentVariableGuid.Format DisplayFormat { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The type of the variable. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs index 542535f..1bc82dd 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs @@ -16,7 +16,7 @@ public class SegmentIntAttribute : Attribute, IResourceAttribute, ISegmentAttrib private string Display { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the variable. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs index 2d7c5a2..07f5058 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs @@ -16,7 +16,7 @@ public class SegmentStringAttribute : Attribute, IResourceAttribute, ISegmentAtt private string Display { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the variable. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs index 408e1dc..f379a4a 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs @@ -16,7 +16,7 @@ public class SegmentUIntAttribute : Attribute, IResourceAttribute, ISegmentAttri private string Display { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the variable. /// The display string. diff --git a/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs index 0543993..25e7376 100644 --- a/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute public class StatusCodeAttribute : System.Attribute, IApplicationAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The status code. public StatusCodeAttribute(int status) diff --git a/src/WebExpress.WebCore/WebAttribute/SystemPluginAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SystemPluginAttribute.cs index 4efd157..1962657 100644 --- a/src/WebExpress.WebCore/WebAttribute/SystemPluginAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SystemPluginAttribute.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebAttribute public class SystemPluginAttribute : Attribute { /// - /// Constructor + /// Initializes a new instance of the class. /// public SystemPluginAttribute() { diff --git a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs index 6fa9aeb..7189ba8 100644 --- a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs @@ -3,7 +3,7 @@ public class TitleAttribute : System.Attribute, IResourceAttribute { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The display text. public TitleAttribute(string display) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs index d46af63..38cc81c 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs @@ -20,7 +20,7 @@ public class ComponentItem public IComponent ComponentInstance { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// internal ComponentItem() { diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index 776d6a8..bcb3f4b 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -55,7 +55,7 @@ public static class ComponentManager ModuleManager, EventManager, JobManager, - ResponseManager, + StatusPageManager, SitemapManager, InternationalizationManager, SessionManager, @@ -105,10 +105,10 @@ public static class ComponentManager public static JobManager JobManager { get; private set; } /// - /// Returns the response manager. + /// Returns the status page manager. /// - /// The instance of the response manager or null. - public static ResponseManager ResponseManager { get; private set; } + /// The instance of the status page manager or null. + public static StatusPageManager StatusPageManager { get; private set; } /// /// Returns the resource manager. @@ -163,7 +163,7 @@ internal static void Initialization(IHttpServerContext httpServerContext) ApplicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; ModuleManager = CreateInstance(typeof(ModuleManager)) as ModuleManager; ResourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; - ResponseManager = CreateInstance(typeof(ResponseManager)) as ResponseManager; + StatusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; SitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; @@ -471,7 +471,7 @@ internal static void LogStatus() ApplicationManager.PrepareForLog(pluginContext, output, 4); ModuleManager.PrepareForLog(pluginContext, output, 4); ResourceManager.PrepareForLog(pluginContext, output, 4); - ResponseManager.PrepareForLog(pluginContext, output, 4); + StatusPageManager.PrepareForLog(pluginContext, output, 4); JobManager.PrepareForLog(pluginContext, output, 4); } diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 80a0965..5fb709c 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -24,7 +24,7 @@ public sealed class EventManager : IComponentPlugin, ISystemComponent private EventDictionary Dictionary { get; } = new EventDictionary(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal EventManager() { diff --git a/src/WebExpress.WebCore/WebHtml/Favicon.cs b/src/WebExpress.WebCore/WebHtml/Favicon.cs index 1a84c9d..3b8964d 100644 --- a/src/WebExpress.WebCore/WebHtml/Favicon.cs +++ b/src/WebExpress.WebCore/WebHtml/Favicon.cs @@ -13,7 +13,7 @@ public class Favicon public TypeFavicon Mediatype { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. /// The media type. @@ -24,7 +24,7 @@ public Favicon(string url, TypeFavicon mediatype) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. /// The media type. @@ -34,7 +34,7 @@ public Favicon(string url) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. /// The media type. diff --git a/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs b/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs index 4f53216..1c0b8e8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs @@ -15,7 +15,7 @@ public class HtmlAttribute : IHtmlAttribute public string Value { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlAttribute() { @@ -23,7 +23,7 @@ public HtmlAttribute() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. public HtmlAttribute(string name) @@ -32,7 +32,7 @@ public HtmlAttribute(string name) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The value. diff --git a/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs b/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs index 54ce20e..33c9419 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs @@ -14,7 +14,7 @@ public class HtmlAttributeNoneValue : IHtmlAttribute public string Name { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlAttributeNoneValue() { @@ -22,7 +22,7 @@ public HtmlAttributeNoneValue() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. public HtmlAttributeNoneValue(string name) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlComment.cs b/src/WebExpress.WebCore/WebHtml/HtmlComment.cs index 36bdd09..9b1cc8a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlComment.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlComment.cs @@ -10,7 +10,7 @@ public class HtmlComment : IHtmlNode public string Text { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlComment() { @@ -18,7 +18,7 @@ public HtmlComment() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The text. public HtmlComment(string text) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs index 2f1a066..e56a539 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs @@ -99,7 +99,7 @@ public string OnClick public bool CloseTag { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the item. public HtmlElement(string name, bool closeTag = true) @@ -109,7 +109,7 @@ public HtmlElement(string name, bool closeTag = true) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name of the item. public HtmlElement(string name, bool closeTag, params IHtml[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs index da10b25..8e5fe89 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs @@ -37,7 +37,7 @@ public string DateTime } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEditDel() : base("del") @@ -46,7 +46,7 @@ public HtmlElementEditDel() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEditDel(string text) @@ -56,7 +56,7 @@ public HtmlElementEditDel(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEditDel(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs index cc832e0..3c1d528 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs @@ -37,7 +37,7 @@ public string DateTime } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEditIns() : base("ins") @@ -46,7 +46,7 @@ public HtmlElementEditIns() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEditIns(string text) @@ -56,7 +56,7 @@ public HtmlElementEditIns(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEditIns(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs index 09ad54e..9b2154b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs @@ -15,7 +15,7 @@ public class HtmlElementEmbeddedEmbed : HtmlElement, IHtmlElementEmbedded public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedEmbed() : base("embed") @@ -24,7 +24,7 @@ public HtmlElementEmbeddedEmbed() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedEmbed(params IHtmlNode[] nodes) @@ -34,7 +34,7 @@ public HtmlElementEmbeddedEmbed(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedEmbed(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs index fdcef6a..02d4da3 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs @@ -14,7 +14,7 @@ public class HtmlElementEmbeddedIframe : HtmlElement, IHtmlElementEmbedded public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedIframe() : base("iframe") @@ -23,7 +23,7 @@ public HtmlElementEmbeddedIframe() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedIframe(params IHtmlNode[] nodes) @@ -33,7 +33,7 @@ public HtmlElementEmbeddedIframe(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedIframe(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs index 2729205..0f71a91 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs @@ -16,7 +16,7 @@ public class HtmlElementEmbeddedObject : HtmlElement, IHtmlElementEmbedded public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedObject() : base("object") @@ -24,7 +24,7 @@ public HtmlElementEmbeddedObject() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedObject(params IHtmlNode[] nodes) @@ -34,7 +34,7 @@ public HtmlElementEmbeddedObject(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedObject(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs index 603f667..d012882 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs @@ -7,7 +7,7 @@ public class HtmlElementEmbeddedParam : HtmlElement, IHtmlElementEmbedded { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedParam() : base("param", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs index 7af7573..069f1d9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs @@ -13,7 +13,7 @@ public class HtmlElementEmbeddedPicture : HtmlElement, IHtmlElementEmbedded public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedPicture() : base("picture") @@ -21,7 +21,7 @@ public HtmlElementEmbeddedPicture() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedPicture(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementEmbeddedPicture(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementEmbeddedPicture(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs index 1e9faab..7bfaab8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs @@ -7,7 +7,7 @@ public class HtmlElementEmbeddedSource : HtmlElement, IHtmlElementEmbedded { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementEmbeddedSource() : base("source", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs index 9605713..1a71187 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebHtml /// /// Represents a button element. /// - public class HtmlElementFieldButton : HtmlElement, IHtmlFormularItem + public class HtmlElementFieldButton : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the name of the input field. @@ -78,7 +78,7 @@ public string Title } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFieldButton() : base("button") @@ -87,7 +87,7 @@ public HtmlElementFieldButton() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldButton(string text) @@ -97,7 +97,7 @@ public HtmlElementFieldButton(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldButton(params IHtmlNode[] nodes) @@ -107,7 +107,7 @@ public HtmlElementFieldButton(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldButton(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs index 12d7f9e..389120b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebHtml /// Represents a field for user input of a specific type. The type (radio button, checkbox, /// text input, etc.) is specified using the type attribute. /// - public class HtmlElementFieldInput : HtmlElement, IHtmlFormularItem + public class HtmlElementFieldInput : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the name of the input field. @@ -181,7 +181,7 @@ public string Form } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFieldInput() : base("input") @@ -190,7 +190,7 @@ public HtmlElementFieldInput() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldInput(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs index d4529b5..27bf088 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml /// /// /// - public class HtmlElementFieldLabel : HtmlElement, IHtmlFormularItem + public class HtmlElementFieldLabel : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the name of the input field. @@ -29,7 +29,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFieldLabel() : base("label") @@ -38,7 +38,7 @@ public HtmlElementFieldLabel() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldLabel(string text) @@ -48,7 +48,7 @@ public HtmlElementFieldLabel(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldLabel(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs index 2a6db11..bfd68c7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs @@ -17,7 +17,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFieldLegend() : base("legend") @@ -25,7 +25,7 @@ public HtmlElementFieldLegend() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldLegend(string text) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs index 8a8db3b..82300e3 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs @@ -10,7 +10,7 @@ namespace WebExpress.WebCore.WebHtml /// /// /// - public class HtmlElementFieldSelect : HtmlElement, IHtmlFormularItem + public class HtmlElementFieldSelect : HtmlElement, IHtmlElementFormItem { /// /// Returns the elements. @@ -63,7 +63,7 @@ public string OnChange } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFieldSelect() : base("select") @@ -72,7 +72,7 @@ public HtmlElementFieldSelect() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFieldSelect(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs index b1de468..bccbc46 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs @@ -14,7 +14,7 @@ public class HtmlElementFormDatalist : HtmlElement, IHtmlElementForm public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormDatalist() : base("datalist") @@ -22,7 +22,7 @@ public HtmlElementFormDatalist() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormDatalist(string text) @@ -31,7 +31,7 @@ public HtmlElementFormDatalist(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormDatalist(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs index ad2b299..48117ed 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebHtml /// /// Represents a set of controls. /// - public class HtmlElementFormFieldset : HtmlElement, IHtmlFormularItem + public class HtmlElementFormFieldset : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the name of the input field. @@ -42,7 +42,7 @@ public string Form public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormFieldset() : base("fieldset") @@ -51,7 +51,7 @@ public HtmlElementFormFieldset() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormFieldset(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs index d16bf37..0fe1444 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs @@ -69,7 +69,7 @@ public string Target public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormForm() : base("form") @@ -77,7 +77,7 @@ public HtmlElementFormForm() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormForm(string text) @@ -86,7 +86,7 @@ public HtmlElementFormForm(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormForm(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs index 9a6e1d6..c836768 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs @@ -5,10 +5,10 @@ namespace WebExpress.WebCore.WebHtml /// /// Represents a control element for generating a pair of public and private keys and sending the public key. /// - public class HtmlElementFormKeygen : HtmlElement, IHtmlFormularItem + public class HtmlElementFormKeygen : HtmlElement, IHtmlElementFormItem { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormKeygen() : base("keygen") @@ -16,7 +16,7 @@ public HtmlElementFormKeygen() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormKeygen(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs index b677bb3..e6f50d8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs @@ -72,7 +72,7 @@ public string Optimum } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormMeter() : base("meter") @@ -80,7 +80,7 @@ public HtmlElementFormMeter() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormMeter(string text) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs index 329b6e5..fb29ee2 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.WebHtml /// /// /// - public class HtmlElementFormOptgroup : HtmlElement, IHtmlFormularItem + public class HtmlElementFormOptgroup : HtmlElement, IHtmlElementFormItem { /// /// Returns the elements. @@ -29,7 +29,7 @@ public string Label } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormOptgroup() : base("optgroup") @@ -37,7 +37,7 @@ public HtmlElementFormOptgroup() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormOptgroup(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs index 100918d..fa84954 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs @@ -10,7 +10,7 @@ namespace WebExpress.WebCore.WebHtml /// /// /// - public class HtmlElementFormOption : HtmlElement, IHtmlFormularItem + public class HtmlElementFormOption : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the text. @@ -40,7 +40,7 @@ public bool Selected } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormOption() : base("option") @@ -48,7 +48,7 @@ public HtmlElementFormOption() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormOption(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs index d96c473..9567bca 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs @@ -5,10 +5,10 @@ namespace WebExpress.WebCore.WebHtml /// /// Represents the result of a calculation. /// - public class HtmlElementFormOutput : HtmlElement, IHtmlFormularItem + public class HtmlElementFormOutput : HtmlElement, IHtmlElementFormItem { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormOutput() : base("output") @@ -16,7 +16,7 @@ public HtmlElementFormOutput() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormOutput(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs index 0041977..f30e1b9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs @@ -45,7 +45,7 @@ public string Max } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormProgress() : base("progress") @@ -53,7 +53,7 @@ public HtmlElementFormProgress() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormProgress(string text) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs index 346164c..c786f77 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebHtml /// /// Represents an element for multi-line text input. /// - public class HtmlElementFormTextarea : HtmlElement, IHtmlFormularItem + public class HtmlElementFormTextarea : HtmlElement, IHtmlElementFormItem { /// /// Returns or sets the name of the input field. @@ -118,7 +118,7 @@ public string Form } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementFormTextarea() : base("textarea") @@ -126,7 +126,7 @@ public HtmlElementFormTextarea() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementFormTextarea(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs index 7360c56..bdf0a87 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs @@ -13,7 +13,7 @@ public class HtmlElementInteractiveCommand : HtmlElement, IHtmlElementInteractiv public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementInteractiveCommand() : base("command") @@ -21,7 +21,7 @@ public HtmlElementInteractiveCommand() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveCommand(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementInteractiveCommand(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveCommand(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs index 40638d5..cb61985 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs @@ -13,7 +13,7 @@ public class HtmlElementInteractiveDetails : HtmlElement, IHtmlElementInteractiv public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementInteractiveDetails() : base("details") @@ -21,7 +21,7 @@ public HtmlElementInteractiveDetails() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveDetails(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementInteractiveDetails(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveDetails(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs index 0a08db6..db8f850 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs @@ -13,7 +13,7 @@ public class HtmlElementInteractiveMenu : HtmlElement, IHtmlElementInteractive public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementInteractiveMenu() : base("menu") @@ -21,7 +21,7 @@ public HtmlElementInteractiveMenu() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveMenu(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementInteractiveMenu(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveMenu(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs index ce8e7c6..282a928 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs @@ -13,7 +13,7 @@ public class HtmlElementInteractiveSummary : HtmlElement, IHtmlElementInteractiv public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementInteractiveSummary() : base("summary") @@ -21,7 +21,7 @@ public HtmlElementInteractiveSummary() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveSummary(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementInteractiveSummary(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementInteractiveSummary(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataBase.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataBase.cs index 219c604..e7afec7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataBase.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataBase.cs @@ -25,7 +25,7 @@ public string Href } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataBase() : base("base") @@ -34,7 +34,7 @@ public HtmlElementMetadataBase() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. public HtmlElementMetadataBase(string url) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs index 9904000..b5a8905 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs @@ -147,7 +147,7 @@ public IEnumerable> Meta private List ElementMeta { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataHead() : base("head") diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataLink.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataLink.cs index c47606b..7114047 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataLink.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataLink.cs @@ -33,7 +33,7 @@ public string Type } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataLink() : base("link", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataMeta.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataMeta.cs index d64440c..9ba147b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataMeta.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataMeta.cs @@ -22,7 +22,7 @@ public string Value } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataMeta() : base("meta") @@ -30,7 +30,7 @@ public HtmlElementMetadataMeta() } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataMeta(string key) : this() @@ -40,7 +40,7 @@ public HtmlElementMetadataMeta(string key) } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataMeta(string key, string value) : this() diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataStyle.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataStyle.cs index be11945..d1c4f79 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataStyle.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataStyle.cs @@ -13,7 +13,7 @@ public class HtmlElementMetadataStyle : HtmlElement, IHtmlElementMetadata public string Code { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataStyle() : base("style") @@ -21,7 +21,7 @@ public HtmlElementMetadataStyle() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The text. public HtmlElementMetadataStyle(string code) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataTitle.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataTitle.cs index 56f3564..d1dda04 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataTitle.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataTitle.cs @@ -14,7 +14,7 @@ public class HtmlElementMetadataTitle : HtmlElement, IHtmlElementMetadata public string Title { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMetadataTitle() : base("title") @@ -22,7 +22,7 @@ public HtmlElementMetadataTitle() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The title. public HtmlElementMetadataTitle(string title) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs index df805a8..99d5030 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs @@ -6,7 +6,7 @@ public class HtmlElementMultimediaArea : HtmlElement, IHtmlElementMultimedia { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaArea() : base("area", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaAudio.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaAudio.cs index 7b9e5d1..89c8482 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaAudio.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaAudio.cs @@ -15,7 +15,7 @@ public string Src } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaAudio() : base("audio", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaImg.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaImg.cs index fa9b055..5e27173 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaImg.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaImg.cs @@ -62,7 +62,7 @@ public string Target } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaImg() : base("img", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs index 681306a..bc6247c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs @@ -6,7 +6,7 @@ public class HtmlElementMultimediaMap : HtmlElement, IHtmlElementMultimedia { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaMap() : base("map", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs index cc47c4d..d0a1e10 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs @@ -13,7 +13,7 @@ public class HtmlElementMultimediaMath : HtmlElement, IHtmlElementMultimedia public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaMath() : base("math") @@ -21,7 +21,7 @@ public HtmlElementMultimediaMath() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementMultimediaMath(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementMultimediaMath(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementMultimediaMath(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs index 1dc5222..283e5f8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs @@ -41,7 +41,7 @@ public string Target } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaSvg() : base("svg") @@ -49,7 +49,7 @@ public HtmlElementMultimediaSvg() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementMultimediaSvg(params IHtmlNode[] nodes) @@ -59,7 +59,7 @@ public HtmlElementMultimediaSvg(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementMultimediaSvg(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs index debba82..e20362c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs @@ -6,7 +6,7 @@ public class HtmlElementMultimediaTrack : HtmlElement, IHtmlElementMultimedia { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaTrack() : base("track", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaVideo.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaVideo.cs index 5ade74a..77e8181 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaVideo.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaVideo.cs @@ -15,7 +15,7 @@ public string Src } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementMultimediaVideo() : base("video", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementRootHtml.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementRootHtml.cs index 754d4c7..9ea0eb1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementRootHtml.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementRootHtml.cs @@ -19,7 +19,7 @@ public class HtmlElementRootHtml : HtmlElement, IHtmlElementRoot public HtmlElementSectionBody Body { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementRootHtml() : base("html") diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingCanvas.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingCanvas.cs index 9ac25ab..af93b06 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingCanvas.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingCanvas.cs @@ -27,7 +27,7 @@ public int Height } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementScriptingCanvas() : base("canvas", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs index 725dfa3..7559f63 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs @@ -13,7 +13,7 @@ public class HtmlElementScriptingNoscript : HtmlElement, IHtmlElementScripting public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementScriptingNoscript() : base("span") @@ -21,7 +21,7 @@ public HtmlElementScriptingNoscript() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementScriptingNoscript(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementScriptingNoscript(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementScriptingNoscript(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingScript.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingScript.cs index abec94d..774a7e0 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingScript.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingScript.cs @@ -40,7 +40,7 @@ public string Src } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementScriptingScript() : base("script") @@ -49,7 +49,7 @@ public HtmlElementScriptingScript() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The text. public HtmlElementScriptingScript(string code) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs index 44f2093..1481aa8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionAddress : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionAddress() : base("address") @@ -21,7 +21,7 @@ public HtmlElementSectionAddress() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionAddress(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementSectionAddress(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionAddress(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs index 5e51121..4b3d40d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionArticle : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionArticle() : base("article") @@ -21,7 +21,7 @@ public HtmlElementSectionArticle() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionArticle(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementSectionArticle(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionArticle(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs index 9a372bb..51c3077 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionAside : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionAside() : base("aside") @@ -21,7 +21,7 @@ public HtmlElementSectionAside() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionAside(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementSectionAside(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionAside(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs index 7823765..ebf58f6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs @@ -44,7 +44,7 @@ public List ScriptLinks private List ElementScriptLinks { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionBody() : base("body") @@ -53,7 +53,7 @@ public HtmlElementSectionBody() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionBody(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs index 7bcb03c..6c4b4df 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs @@ -23,7 +23,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionFooter() : base("footer") @@ -31,7 +31,7 @@ public HtmlElementSectionFooter() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionFooter(string text) @@ -41,7 +41,7 @@ public HtmlElementSectionFooter(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionFooter(params IHtmlNode[] nodes) @@ -51,7 +51,7 @@ public HtmlElementSectionFooter(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionFooter(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs index 579471f..7f5b196 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH1() : base("h1") @@ -27,7 +27,7 @@ public HtmlElementSectionH1() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH1(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH1(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH1(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs index 7fdec3b..804a42c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH2() : base("h2") @@ -27,7 +27,7 @@ public HtmlElementSectionH2() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH2(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH2(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH2(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs index b226a40..ea61c66 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH3() : base("h3") @@ -27,7 +27,7 @@ public HtmlElementSectionH3() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH3(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH3(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH3(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs index b522df3..1597f78 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH4() : base("h4") @@ -27,7 +27,7 @@ public HtmlElementSectionH4() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH4(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH4(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH4(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs index c91e6db..bc87c0f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH5() : base("h5") @@ -27,7 +27,7 @@ public HtmlElementSectionH5() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH5(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH5(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH5(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs index e49196c..0ef3d64 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionH6() : base("h6") @@ -27,7 +27,7 @@ public HtmlElementSectionH6() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH6(string text) @@ -37,7 +37,7 @@ public HtmlElementSectionH6(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionH6(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs index 4fb0d14..e998cd6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionHeader : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionHeader() : base("header") @@ -23,7 +23,7 @@ public HtmlElementSectionHeader() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionHeader(params IHtmlNode[] nodes) @@ -33,7 +33,7 @@ public HtmlElementSectionHeader(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionHeader(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs index 21e64dc..57fd2be 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionMain : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionMain() : base("main") @@ -22,7 +22,7 @@ public HtmlElementSectionMain() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionMain(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementSectionMain(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionMain(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs index c57d478..a77e8b7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionNav : HtmlElement public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionNav() : base("nav") @@ -21,7 +21,7 @@ public HtmlElementSectionNav() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionNav(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementSectionNav(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionNav(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs index 3c3ca21..2814246 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs @@ -13,7 +13,7 @@ public class HtmlElementSectionSection : HtmlElement, IHtmlElementSection public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementSectionSection() : base("section") @@ -21,7 +21,7 @@ public HtmlElementSectionSection() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionSection(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementSectionSection(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementSectionSection(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs index 50c8cf3..f4d1f50 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableCaption() : base("caption") @@ -26,7 +26,7 @@ public HtmlElementTableCaption() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableCaption(string text) @@ -36,7 +36,7 @@ public HtmlElementTableCaption(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableCaption(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCol.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCol.cs index e50b906..77eda4b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCol.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCol.cs @@ -6,7 +6,7 @@ public class HtmlElementTableCol : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableCol() : base("col") diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableColgroup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableColgroup.cs index 33568e2..57addab 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableColgroup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableColgroup.cs @@ -6,7 +6,7 @@ public class HtmlElementTableColgroup : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableColgroup() : base("colgroup") diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs index 7f54c2b..9f134d8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs @@ -19,7 +19,7 @@ public class HtmlElementTableTable : HtmlElement, IHtmlElementTable public List Rows { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTable() : base("table") diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs index d7559bc..a535232 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableTbody : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTbody() : base("tbody") @@ -16,7 +16,7 @@ public HtmlElementTableTbody() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTbody(params IHtmlNode[] nodes) @@ -26,7 +26,7 @@ public HtmlElementTableTbody(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTbody(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs index 17b2779..7d99e13 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableTd : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTd() : base("td") @@ -16,7 +16,7 @@ public HtmlElementTableTd() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTd(IHtmlNode node) @@ -26,7 +26,7 @@ public HtmlElementTableTd(IHtmlNode node) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTd(params IHtmlNode[] nodes) @@ -36,7 +36,7 @@ public HtmlElementTableTd(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTd(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs index 6a3c1f2..d09a356 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableTfoot : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTfoot() : base("tfoot") @@ -16,7 +16,7 @@ public HtmlElementTableTfoot() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTfoot(params IHtmlNode[] nodes) @@ -26,7 +26,7 @@ public HtmlElementTableTfoot(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTfoot(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs index 8f5623c..04f6d39 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableTh : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTh() : base("th") @@ -16,7 +16,7 @@ public HtmlElementTableTh() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTh(params IHtmlNode[] nodes) @@ -26,7 +26,7 @@ public HtmlElementTableTh(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTh(List nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs index fa56102..a69822d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableThead : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableThead() : base("thead") @@ -16,7 +16,7 @@ public HtmlElementTableThead() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableThead(params IHtmlNode[] nodes) @@ -26,7 +26,7 @@ public HtmlElementTableThead(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableThead(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs index 3010a18..fae7d47 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTableTr : HtmlElement, IHtmlElementTable { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTableTr() : base("tr") @@ -16,7 +16,7 @@ public HtmlElementTableTr() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTr(params IHtmlNode[] nodes) @@ -26,7 +26,7 @@ public HtmlElementTableTr(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTableTr(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs index f8905ae..6e258dd 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentBlockquote : HtmlElement, IHtmlElementTextCon public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentBlockquote() : base("blockquote") @@ -21,7 +21,7 @@ public HtmlElementTextContentBlockquote() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentBlockquote(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentBlockquote(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentBlockquote(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs index 3181f98..6cd2b7c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentDd : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentDd() : base("dd") @@ -22,7 +22,7 @@ public HtmlElementTextContentDd() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDd(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextContentDd(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDd(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs index 4d14971..25215ef 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentDiv : HtmlElement public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentDiv() : base("div") @@ -22,7 +22,7 @@ public HtmlElementTextContentDiv() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDiv(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextContentDiv(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDiv(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs index 495059d..05e793e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentDl : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentDl() : base("dl") @@ -22,7 +22,7 @@ public HtmlElementTextContentDl() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDl(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextContentDl(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDl(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs index a9e9ee8..58a6f77 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentDt : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentDt() : base("dt") @@ -21,7 +21,7 @@ public HtmlElementTextContentDt() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDt(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentDt(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentDt(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs index 157eca9..9861bde 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentFigcaption : HtmlElement, IHtmlElementTextCon public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentFigcaption() : base("figcaption") @@ -21,7 +21,7 @@ public HtmlElementTextContentFigcaption() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentFigcaption(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentFigcaption(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentFigcaption(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs index c88f9c0..653e752 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentFigure : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentFigure() : base("figure") @@ -21,7 +21,7 @@ public HtmlElementTextContentFigure() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentFigure(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentFigure(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentFigure(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentHr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentHr.cs index 1c65c2f..9c1ef18 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentHr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentHr.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml public class HtmlElementTextContentHr : HtmlElement, IHtmlElementTextContent { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentHr() : base("hr", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs index 5398e4f..e5cb6c8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentLi : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentLi() : base("li") @@ -21,7 +21,7 @@ public HtmlElementTextContentLi() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentLi(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentLi(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentLi(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs index 3a74f31..27aec2a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs @@ -15,7 +15,7 @@ public class HtmlElementTextContentOl : HtmlElement, IHtmlElementTextContent public new List Elements { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentOl() : base("ol") @@ -24,7 +24,7 @@ public HtmlElementTextContentOl() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentOl(params HtmlElementTextContentLi[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs index fb0ecac..356ec72 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentP() : base("p") @@ -27,7 +27,7 @@ public HtmlElementTextContentP() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentP(string text) @@ -37,7 +37,7 @@ public HtmlElementTextContentP(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentP(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs index 4fdfb6c..b1f5976 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs @@ -13,7 +13,7 @@ public class HtmlElementTextContentPre : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentPre() : base("pre") @@ -21,7 +21,7 @@ public HtmlElementTextContentPre() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentPre(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextContentPre(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentPre(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs index 5eb1978..e088ccf 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs @@ -14,7 +14,7 @@ public class HtmlElementTextContentUl : HtmlElement, IHtmlElementTextContent public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextContentUl() : base("ul") @@ -22,7 +22,7 @@ public HtmlElementTextContentUl() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentUl(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextContentUl(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextContentUl(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs index 76a041d..ac2aa95 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs @@ -60,7 +60,7 @@ public TypeTarget Target } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsA() : base("a") @@ -69,7 +69,7 @@ public HtmlElementTextSemanticsA() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsA(string text) @@ -79,7 +79,7 @@ public HtmlElementTextSemanticsA(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsA(params IHtmlNode[] nodes) @@ -89,7 +89,7 @@ public HtmlElementTextSemanticsA(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsA(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs index 17c06c4..dc27959 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsAbbr : HtmlElement, IHtmlElementTextSemanti public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsAbbr() : base("abbr") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsAbbr() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsAbbr(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsAbbr(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsAbbr(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs index d006851..3e9f491 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs @@ -19,7 +19,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsB() : base("b") @@ -28,7 +28,7 @@ public HtmlElementTextSemanticsB() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsB(string text) @@ -38,7 +38,7 @@ public HtmlElementTextSemanticsB(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsB(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs index 1a39e76..7220fe6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs @@ -28,7 +28,7 @@ public string Dir } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsBdi() : base("bdi") @@ -37,7 +37,7 @@ public HtmlElementTextSemanticsBdi() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsBdi(string text) @@ -47,7 +47,7 @@ public HtmlElementTextSemanticsBdi(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsBdi(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs index f993a57..f6ba5ff 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs @@ -27,7 +27,7 @@ public string Dir } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsBdo() : base("bdo") @@ -36,7 +36,7 @@ public HtmlElementTextSemanticsBdo() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsBdo(string text) @@ -46,7 +46,7 @@ public HtmlElementTextSemanticsBdo(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsBdo(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBr.cs index 9d468b1..8817b2e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBr.cs @@ -6,7 +6,7 @@ public class HtmlElementTextSemanticsBr : HtmlElement, IHtmlElementTextSemantics { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsBr() : base("br", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs index f558ffc..44660a5 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs @@ -23,7 +23,7 @@ public string Text public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsCite() : base("cite") @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsCite() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsCite(string text) @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsCite(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsCite(params IHtmlNode[] nodes) @@ -52,7 +52,7 @@ public HtmlElementTextSemanticsCite(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsCite(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs index 5907db0..37ac379 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsCode : HtmlElement, IHtmlElementTextSemanti public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsCode() : base("code") @@ -22,7 +22,7 @@ public HtmlElementTextSemanticsCode() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsCode(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsCode(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsCode(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs index 7f8e329..87111eb 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs @@ -24,7 +24,7 @@ public string Value } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsData() : base("data") @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsData() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsData(params IHtmlNode[] nodes) @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsData(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsData(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs index 41a3394..f51f335 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsDfn : HtmlElement, IHtmlElementTextSemantic public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsDfn() : base("dfn") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsDfn() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsDfn(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsDfn(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsDfn(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs index 274a6a9..23b0fc6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsEm : HtmlElement, IHtmlElementTextSemantics public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsEm() : base("em") @@ -22,7 +22,7 @@ public HtmlElementTextSemanticsEm() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsEm(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsEm(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsEm(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs index 465265b..901cb42 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs @@ -21,7 +21,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsI() : base("i") @@ -29,7 +29,7 @@ public HtmlElementTextSemanticsI() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsI(string text) @@ -39,7 +39,7 @@ public HtmlElementTextSemanticsI(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsI(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs index de93eec..86e8609 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsKdb : HtmlElement, IHtmlElementTextSemantic public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsKdb() : base("kdb") @@ -22,7 +22,7 @@ public HtmlElementTextSemanticsKdb() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsKdb(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsKdb(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsKdb(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs index 0cce976..d4a7d66 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsMark() : base("mark") @@ -26,7 +26,7 @@ public HtmlElementTextSemanticsMark() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsMark(string text) @@ -36,7 +36,7 @@ public HtmlElementTextSemanticsMark(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsMark(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs index 18ef751..e95ece0 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsQ : HtmlElement, IHtmlElementTextSemantics public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsQ() : base("q") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsQ() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsQ(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsQ(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsQ(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs index e9d4b46..0391e99 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs @@ -19,7 +19,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsRp() : base("rp") @@ -27,7 +27,7 @@ public HtmlElementTextSemanticsRp() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRp(string text) @@ -37,7 +37,7 @@ public HtmlElementTextSemanticsRp(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRp(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs index c062aba..32a9ade 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsRt() : base("rt") @@ -26,7 +26,7 @@ public HtmlElementTextSemanticsRt() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRt(string text) @@ -36,7 +36,7 @@ public HtmlElementTextSemanticsRt(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRt(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs index f1fec38..b84f3d8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsRuby : HtmlElement, IHtmlElementTextSemanti public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsRuby() : base("ruby") @@ -22,7 +22,7 @@ public HtmlElementTextSemanticsRuby() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRuby(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsRuby(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsRuby(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs index b6733e4..203bee5 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsS() : base("s") @@ -26,7 +26,7 @@ public HtmlElementTextSemanticsS() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsS(string text) @@ -36,7 +36,7 @@ public HtmlElementTextSemanticsS(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsS(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs index 2460006..5a80e4e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsSamp : HtmlElement, IHtmlElementTextSemanti public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsSamp() : base("samp") @@ -22,7 +22,7 @@ public HtmlElementTextSemanticsSamp() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSamp(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsSamp(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSamp(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs index 798faab..b258aec 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs @@ -19,7 +19,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsSmall() : base("small") @@ -28,7 +28,7 @@ public HtmlElementTextSemanticsSmall() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSmall(string text) @@ -38,7 +38,7 @@ public HtmlElementTextSemanticsSmall(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSmall(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs index 38223fa..0960230 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsSpan : HtmlElement, IHtmlElementTextSemanti public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsSpan() : base("span") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsSpan() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSpan(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsSpan(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSpan(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs index 712f0af..1eaff7c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs @@ -18,7 +18,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsStrong() : base("strong") @@ -26,7 +26,7 @@ public HtmlElementTextSemanticsStrong() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsStrong(string text) @@ -36,7 +36,7 @@ public HtmlElementTextSemanticsStrong(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsStrong(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs index 0f3f39a..43a740e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsSub : HtmlElement, IHtmlElementTextSemantic public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsSub() : base("sub") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsSub() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSub(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsSub(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSub(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs index 214cf00..a344f4e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs @@ -13,7 +13,7 @@ public class HtmlElementTextSemanticsSup : HtmlElement, IHtmlElementTextSemantic public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsSup() : base("sup") @@ -21,7 +21,7 @@ public HtmlElementTextSemanticsSup() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSup(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementTextSemanticsSup(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsSup(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs index 3796318..7f1a825 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs @@ -18,7 +18,7 @@ public string Time } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsTime() : base("time") @@ -26,7 +26,7 @@ public HtmlElementTextSemanticsTime() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsTime(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs index b83f890..21c2673 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs @@ -20,7 +20,7 @@ public string Text } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsU() : base("u") @@ -28,7 +28,7 @@ public HtmlElementTextSemanticsU() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsU(string text) @@ -38,7 +38,7 @@ public HtmlElementTextSemanticsU(string text) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsU(params IHtmlNode[] nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs index 0f42451..74e964a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs @@ -15,7 +15,7 @@ public class HtmlElementTextSemanticsVar : HtmlElement, IHtmlElementTextSemantic public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsVar() : base("var") @@ -23,7 +23,7 @@ public HtmlElementTextSemanticsVar() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsVar(params IHtmlNode[] nodes) @@ -33,7 +33,7 @@ public HtmlElementTextSemanticsVar(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementTextSemanticsVar(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsWbr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsWbr.cs index fd8303d..5d55ebe 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsWbr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsWbr.cs @@ -7,7 +7,7 @@ public class HtmlElementTextSemanticsWbr : HtmlElement, IHtmlElementTextSemantics { /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementTextSemanticsWbr() : base("wbr", false) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs index b4d4fc7..7df295d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs @@ -13,7 +13,7 @@ public class HtmlElementWebFragmentsSlot : HtmlElement, IHtmlElementWebFragments public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementWebFragmentsSlot() : base("slot") @@ -21,7 +21,7 @@ public HtmlElementWebFragmentsSlot() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementWebFragmentsSlot(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementWebFragmentsSlot(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementWebFragmentsSlot(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs index 77002bb..b3b32f2 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs @@ -13,7 +13,7 @@ public class HtmlElementWebFragmentsTemplate : HtmlElement, IHtmlElementWebFragm public new List Elements => base.Elements; /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlElementWebFragmentsTemplate() : base("template") @@ -21,7 +21,7 @@ public HtmlElementWebFragmentsTemplate() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementWebFragmentsTemplate(params IHtmlNode[] nodes) @@ -31,7 +31,7 @@ public HtmlElementWebFragmentsTemplate(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlElementWebFragmentsTemplate(IEnumerable nodes) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs b/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs index 97f1424..d0325e5 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs @@ -10,7 +10,7 @@ public class HtmlEmpty : IHtmlNode public string Value { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlEmpty() { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlList.cs b/src/WebExpress.WebCore/WebHtml/HtmlList.cs index a59d567..62f7983 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlList.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlList.cs @@ -14,7 +14,7 @@ public class HtmlList : IHtmlNode public List Elements { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlList() { @@ -22,7 +22,7 @@ public HtmlList() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlList(params IHtmlNode[] nodes) @@ -32,7 +32,7 @@ public HtmlList(params IHtmlNode[] nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The first content of the html element. /// The following contents of the html elements. @@ -44,7 +44,7 @@ public HtmlList(IHtmlNode firstNode, params IHtmlNode[] followingNodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The content of the html element. public HtmlList(IEnumerable nodes) @@ -54,7 +54,7 @@ public HtmlList(IEnumerable nodes) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The first content of the html element. /// The following contents of the html elements. diff --git a/src/WebExpress.WebCore/WebHtml/HtmlNbsp.cs b/src/WebExpress.WebCore/WebHtml/HtmlNbsp.cs index 7963300..7d77912 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlNbsp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlNbsp.cs @@ -13,7 +13,7 @@ public class HtmlNbsp : IHtmlNode public string Value { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlNbsp() { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs b/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs index f44c3df..0a45db8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs @@ -10,7 +10,7 @@ public class HtmlRaw : IHtmlNode public string Html { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlRaw() { @@ -18,7 +18,7 @@ public HtmlRaw() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The text. public HtmlRaw(string html) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlText.cs b/src/WebExpress.WebCore/WebHtml/HtmlText.cs index a528f66..a40a845 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlText.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlText.cs @@ -13,14 +13,14 @@ public class HtmlText : IHtmlNode public string Value { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public HtmlText() { } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The text. public HtmlText(string value) diff --git a/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs b/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs new file mode 100644 index 0000000..e18dad3 --- /dev/null +++ b/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs @@ -0,0 +1,6 @@ +namespace WebExpress.WebCore.WebHtml +{ + public interface IHtmlElementFormItem : IHtmlElementForm + { + } +} diff --git a/src/WebExpress.WebCore/WebHtml/IHtmlFormularItem.cs b/src/WebExpress.WebCore/WebHtml/IHtmlFormularItem.cs deleted file mode 100644 index 1de6b0a..0000000 --- a/src/WebExpress.WebCore/WebHtml/IHtmlFormularItem.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace WebExpress.WebCore.WebHtml -{ - public interface IHtmlFormularItem : IHtmlElementForm - { - } -} diff --git a/src/WebExpress.WebCore/WebJob/Clock.cs b/src/WebExpress.WebCore/WebJob/Clock.cs index e0dc26e..a35d5ca 100644 --- a/src/WebExpress.WebCore/WebJob/Clock.cs +++ b/src/WebExpress.WebCore/WebJob/Clock.cs @@ -36,7 +36,7 @@ public class Clock public int Weekday => (int)DateTime.DayOfWeek; /// - /// Constructor + /// Initializes a new instance of the class. /// public Clock() { @@ -46,7 +46,7 @@ public Clock() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The time to copy. public Clock(DateTime dateTime) diff --git a/src/WebExpress.WebCore/WebJob/Cron.cs b/src/WebExpress.WebCore/WebJob/Cron.cs index 684c3db..43312b8 100644 --- a/src/WebExpress.WebCore/WebJob/Cron.cs +++ b/src/WebExpress.WebCore/WebJob/Cron.cs @@ -42,7 +42,7 @@ public class Cron private List Weekday { get; } = new List(); /// - /// Constructor + /// Initializes a new instance of the class. /// /// The reference to the host's context. /// The minute 0-59 or * for any. Comma seperated values or ranges (-) are also possible. diff --git a/src/WebExpress.WebCore/WebJob/JobContext.cs b/src/WebExpress.WebCore/WebJob/JobContext.cs index 80a8c3e..ac084db 100644 --- a/src/WebExpress.WebCore/WebJob/JobContext.cs +++ b/src/WebExpress.WebCore/WebJob/JobContext.cs @@ -26,7 +26,7 @@ public class JobContext : IJobContext public Cron Cron { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The module context. internal JobContext(IModuleContext moduleContext) @@ -36,7 +36,7 @@ internal JobContext(IModuleContext moduleContext) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The plugin context. internal JobContext(IPluginContext pluginContext) diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 534751f..44c62f1 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -42,7 +42,7 @@ public sealed class JobManager : IComponentPlugin, ISystemComponent, IExecutable private IEnumerable DynamicScheduleList { get; set; } = new List(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal JobManager() { diff --git a/src/WebExpress.WebCore/WebLog/Log.cs b/src/WebExpress.WebCore/WebLog/Log.cs index 5df94ae..0074713 100644 --- a/src/WebExpress.WebCore/WebLog/Log.cs +++ b/src/WebExpress.WebCore/WebLog/Log.cs @@ -133,7 +133,7 @@ private int Width } /// - /// Constructor + /// Initializes a new instance of the class. /// public Log() { diff --git a/src/WebExpress.WebCore/WebLog/LogFrame.cs b/src/WebExpress.WebCore/WebLog/LogFrame.cs index c90d3a2..130e24d 100644 --- a/src/WebExpress.WebCore/WebLog/LogFrame.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrame.cs @@ -34,7 +34,7 @@ public class LogFrame : IDisposable protected Log Log { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The log entry. /// The name. @@ -54,7 +54,7 @@ public LogFrame(Log log, string name, string additionalHeading = null, [CallerMe } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The additional heading or zero. diff --git a/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs index 6c049c1..d2153a4 100644 --- a/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs @@ -29,7 +29,7 @@ public class LogFrameSimple : IDisposable protected ILog Log { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The log entry. /// Method that wants to log. diff --git a/src/WebExpress.WebCore/WebLog/LogItem.cs b/src/WebExpress.WebCore/WebLog/LogItem.cs index 3cd261d..cc45ef1 100644 --- a/src/WebExpress.WebCore/WebLog/LogItem.cs +++ b/src/WebExpress.WebCore/WebLog/LogItem.cs @@ -28,7 +28,7 @@ internal class LogItem private readonly DateTime m_timestamp; /// - /// Constructor + /// Initializes a new instance of the class. /// /// The level. /// The modul/funktion. diff --git a/src/WebExpress.WebCore/WebMessage/HttpContext.cs b/src/WebExpress.WebCore/WebMessage/HttpContext.cs index e885161..62a17e6 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpContext.cs @@ -49,7 +49,7 @@ public class HttpContext public Uri Uri { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// internal HttpContext() { @@ -57,7 +57,7 @@ internal HttpContext() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// Initial set of features. /// The context of the Web server. diff --git a/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs b/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs index f7c11f8..c2ff313 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs @@ -12,7 +12,7 @@ public class HttpExceptionContext : HttpContext public Exception Exception { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// An exception that prevented the creation of the context. /// Initial set of features. diff --git a/src/WebExpress.WebCore/WebMessage/Parameter.cs b/src/WebExpress.WebCore/WebMessage/Parameter.cs index 6dd5a35..575f4b3 100644 --- a/src/WebExpress.WebCore/WebMessage/Parameter.cs +++ b/src/WebExpress.WebCore/WebMessage/Parameter.cs @@ -22,14 +22,14 @@ public class Parameter public string Value { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public Parameter() { } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. @@ -48,7 +48,7 @@ public Parameter(string key, Guid value, ParameterScope scope) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. @@ -67,7 +67,7 @@ public Parameter(string key, string value, ParameterScope scope) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. @@ -80,7 +80,7 @@ public Parameter(string key, int value, ParameterScope scope) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. diff --git a/src/WebExpress.WebCore/WebMessage/ParameterFile.cs b/src/WebExpress.WebCore/WebMessage/ParameterFile.cs index 41bb840..233a991 100644 --- a/src/WebExpress.WebCore/WebMessage/ParameterFile.cs +++ b/src/WebExpress.WebCore/WebMessage/ParameterFile.cs @@ -14,14 +14,14 @@ public class ParameterFile : Parameter /// - /// Constructor + /// Initializes a new instance of the class. /// public ParameterFile() { } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. @@ -32,7 +32,7 @@ public ParameterFile(string key, string value, ParameterScope scope) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. @@ -43,7 +43,7 @@ public ParameterFile(string key, int value, ParameterScope scope) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index adbeb2e..83e3cf7 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -129,7 +129,7 @@ public CultureInfo Culture public byte[] Content { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// Initial set of features. /// The context of the web server. diff --git a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs index 9d9c2e7..888866d 100644 --- a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs @@ -80,7 +80,7 @@ public class RequestHeaderFields public string Referer { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// Initial set of features. internal RequestHeaderFields(IFeatureCollection contextFeatures) diff --git a/src/WebExpress.WebCore/WebMessage/Response.cs b/src/WebExpress.WebCore/WebMessage/Response.cs index 46f9f82..633bec2 100644 --- a/src/WebExpress.WebCore/WebMessage/Response.cs +++ b/src/WebExpress.WebCore/WebMessage/Response.cs @@ -26,7 +26,7 @@ public class Response public string Reason { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// protected Response() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs index f147e28..950a6e4 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs @@ -6,7 +6,7 @@ public class ResponseBadRequest : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseBadRequest() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs index 7e2ff3b..459a187 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs @@ -6,7 +6,7 @@ public class ResponseForbidden : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseForbidden() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs index f0f85ba..e543b16 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs @@ -55,7 +55,7 @@ public class ResponseHeaderFields public CookieCollection Cookies { get; } = new CookieCollection(); /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseHeaderFields() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs index 3d469c9..5d2dcdc 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs @@ -6,7 +6,7 @@ public class ResponseInternalServerError : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseInternalServerError() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs index 12b6011..72b8f02 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs @@ -6,7 +6,7 @@ public class ResponseNotFound : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseNotFound() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseOK.cs b/src/WebExpress.WebCore/WebMessage/ResponseOK.cs index 606ce6c..cf4b830 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseOK.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseOK.cs @@ -6,7 +6,7 @@ public class ResponseOK : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseOK() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs b/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs index 0fbfb81..b4ccd71 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs @@ -6,7 +6,7 @@ public class ResponseRedirectPermanentlyMoved : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseRedirectPermanentlyMoved(string location) { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs b/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs index a50c499..1d00b4b 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs @@ -6,7 +6,7 @@ public class ResponseRedirectTemporarilyMoved : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseRedirectTemporarilyMoved(string location) { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs index b7e6c32..6c6a093 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs @@ -6,7 +6,7 @@ public class ResponseUnauthorized : Response { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResponseUnauthorized() { diff --git a/src/WebExpress.WebCore/WebModule/IModule.cs b/src/WebExpress.WebCore/WebModule/IModule.cs index c917775..e68e9c0 100644 --- a/src/WebExpress.WebCore/WebModule/IModule.cs +++ b/src/WebExpress.WebCore/WebModule/IModule.cs @@ -5,14 +5,9 @@ namespace WebExpress.WebCore.WebModule public interface IModule : IDisposable { /// - /// Returns the context of the module. + /// Instillation of the module. Here, for example, managed resources can be loaded. /// - IModuleContext ModuleContext { get; } - - /// - /// Initialization of the module. - /// - /// The context. + /// The module context. void Initialization(IModuleContext moduleContext); /// diff --git a/src/WebExpress.WebCore/WebModule/ModuleContext.cs b/src/WebExpress.WebCore/WebModule/ModuleContext.cs index 155f2ea..f595b0f 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleContext.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleContext.cs @@ -52,9 +52,9 @@ public class ModuleContext : IModuleContext public UriResource Icon { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// - public ModuleContext() + internal ModuleContext() { } diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index fe9dced..5db05fe 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -44,7 +44,7 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst .Select(x => x.ModuleContext); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// internal ModuleManager() { diff --git a/src/WebExpress.WebCore/WebPackage/PackageItem.cs b/src/WebExpress.WebCore/WebPackage/PackageItem.cs index 33ac150..7090cbc 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageItem.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageItem.cs @@ -60,7 +60,7 @@ public class PackageItem public IEnumerable PluginSources { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// internal PackageItem() { diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index f56b876..f142ea4 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -47,7 +47,7 @@ public sealed class PackageManager : IComponent, ISystemComponent private PackageCatalog Catalog { get; } = new PackageCatalog(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal PackageManager() { diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index 622738c..e98895c 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -4,6 +4,11 @@ namespace WebExpress.WebCore.WebPage { public interface IPage : IResource { + /// + /// Returns the resource context where the resource exists. + /// + public IResourceContext ResourceContext { get; } + /// /// Returns or sets the page title. /// diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index 0bfceae..9082b40 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -15,7 +15,7 @@ namespace WebExpress.WebCore.WebPage public string Title { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public Page() { diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 6dd4111..50853c5 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,15 +1,15 @@ using System.Globalization; -using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage { - public class RenderContext : II18N + public class RenderContext { /// - /// The page where the control is rendered. + /// Returns the page where is rendered. /// public IPage Page { get; internal set; } @@ -19,55 +19,41 @@ public class RenderContext : II18N public Request Request { get; internal set; } /// - /// Returns the host context. + /// The uri of the request. /// - public IHttpServerContext Host => Request.ServerContext; + public UriResource Uri => Request?.Uri; /// - /// The uri of the request. + /// Returns the culture. /// - public UriResource Uri => Request.Uri; - - ///// - ///// Returns the context path. - ///// - //public UriResource ContextPath => Page?.ResourceContext?.ContextPath; + public CultureInfo Culture => Request?.Culture; /// - /// Returns the culture. + /// Provides the context of the associated plugin. /// - public CultureInfo Culture - { - get { return Page?.Culture; } - set { } - } + public IPluginContext PluginContext => Page?.ResourceContext?.PluginContext; /// /// Provides the context of the associated application. /// - public IApplicationContext ApplicationContext => Page?.ResourceContext.ApplicationContext; + public IApplicationContext ApplicationContext => Page?.ResourceContext?.ApplicationContext; /// /// Returns the contents of a page. /// public IVisualTree VisualTree { get; protected set; } - ///// - ///// Returns the log for writing status messages to the console and to a log file. - ///// - //public Log Log { get; private set; } - /// - /// Constructor + /// Initializes a new instance of the class. /// public RenderContext() { } /// - /// Constructor + /// Initializes a new instance of the class. /// - /// The page where the control is rendered. + /// The page where is rendered. /// The request. /// The visual tree. public RenderContext(IPage page, Request request, IVisualTree visualTree) @@ -75,7 +61,6 @@ public RenderContext(IPage page, Request request, IVisualTree visualTree) Page = page; Request = request; VisualTree = visualTree; - Culture = Page.Culture; } /// diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index 3e8baa0..78b43a0 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -57,7 +57,7 @@ public abstract class VisualTree : IVisualTree public IHtmlNode Content { get; } /// - /// Constructor + /// Initializes a new instance of the class. /// public VisualTree() { diff --git a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs index f46cef0..c89b617 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs @@ -8,14 +8,9 @@ namespace WebExpress.WebCore.WebPlugin public interface IPlugin : IDisposable { /// - /// Returns the context of the plugin. + /// Initialization of the plugin. Here, for example, managed resources can be loaded. /// - IPluginContext PluginContext { get; } - - /// - /// Initialization of the plugin. - /// - /// The context. + /// The plugin context. void Initialization(IPluginContext pluginContext); /// diff --git a/src/WebExpress.WebCore/WebPlugin/Plugin.cs b/src/WebExpress.WebCore/WebPlugin/Plugin.cs index 677991b..4e7406d 100644 --- a/src/WebExpress.WebCore/WebPlugin/Plugin.cs +++ b/src/WebExpress.WebCore/WebPlugin/Plugin.cs @@ -11,7 +11,7 @@ public abstract class Plugin : IPlugin public IPluginContext PluginContext { get; private set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public Plugin() { diff --git a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs index cb37966..cb4783c 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs @@ -56,7 +56,7 @@ public class PluginContext : IPluginContext public IHttpServerContext Host { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public PluginContext() { diff --git a/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs b/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs index 5cc5865..8a95b29 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs @@ -16,7 +16,7 @@ public class PluginLoadContext : AssemblyLoadContext private AssemblyDependencyResolver Resolver { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The base path of the plugin. public PluginLoadContext(string pluginPath) diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 3855632..43019d2 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -48,7 +48,7 @@ public class PluginManager : IComponent, IExecutableElements, ISystemComponent public IEnumerable Plugins => Dictionary.Values.Select(x => x.PluginContext).ToList(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal PluginManager() { @@ -187,8 +187,7 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext foreach (var type in assembly .GetExportedTypes() .Where(x => x.IsClass && x.IsSealed) - .Where(x => x.GetInterface(typeof(IPlugin).Name) != null) - .Where(x => x.Name.Equals("Plugin"))) + .Where(x => x.GetInterface(typeof(IPlugin).Name) != null)) { var id = type.Namespace?.ToLower(); var name = type.Assembly.GetCustomAttribute()?.Title; @@ -225,8 +224,7 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext PluginName = name, Manufacturer = type.Assembly.GetCustomAttribute()?.Company, Copyright = type.Assembly.GetCustomAttribute()?.Copyright, - //License = type.Assembly.GetCustomAttribute()?.Copyright, - Icon = UriResource.Combine(HttpServerContext.ContextPath, icon), + Icon = UriResource.Combine(HttpServerContext?.ContextPath, icon), Description = description, Version = type.Assembly.GetCustomAttribute()?.InformationalVersion, Host = HttpServerContext diff --git a/src/WebExpress.WebCore/WebResource/IResource.cs b/src/WebExpress.WebCore/WebResource/IResource.cs index 50d209f..fb973b3 100644 --- a/src/WebExpress.WebCore/WebResource/IResource.cs +++ b/src/WebExpress.WebCore/WebResource/IResource.cs @@ -6,12 +6,7 @@ namespace WebExpress.WebCore.WebResource public interface IResource : II18N { /// - /// Returns the module context where the resource exists. - /// - IResourceContext ResourceContext { get; } - - /// - /// Initialization + /// Instillation of the resource. Here, for example, managed resources can be loaded. /// /// The context of the resource. void Initialization(IResourceContext resourceContext); diff --git a/src/WebExpress.WebCore/WebResource/RedirectException.cs b/src/WebExpress.WebCore/WebResource/RedirectException.cs index 1ab36ae..fe25760 100644 --- a/src/WebExpress.WebCore/WebResource/RedirectException.cs +++ b/src/WebExpress.WebCore/WebResource/RedirectException.cs @@ -15,7 +15,7 @@ public class RedirectException : Exception public bool Permanet { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// Das Weiterleitungsziel /// true wenn 301 gesendet werden soll, flase für 302 diff --git a/src/WebExpress.WebCore/WebResource/Resource.cs b/src/WebExpress.WebCore/WebResource/Resource.cs index 1e4a81c..fb0895f 100644 --- a/src/WebExpress.WebCore/WebResource/Resource.cs +++ b/src/WebExpress.WebCore/WebResource/Resource.cs @@ -33,7 +33,7 @@ public abstract class Resource : IResource public CultureInfo Culture { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public Resource() { diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index 336cad6..cfdb1a0 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -23,7 +23,7 @@ public class ResourceAsset : ResourceBinary public string AssetDirectory { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public ResourceAsset() { diff --git a/src/WebExpress.WebCore/WebResource/ResourceBinary.cs b/src/WebExpress.WebCore/WebResource/ResourceBinary.cs index c35de9c..faa9b2a 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceBinary.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceBinary.cs @@ -13,7 +13,7 @@ public abstract class ResourceBinary : Resource public byte[] Data { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public ResourceBinary() { diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index d5fd449..bcc73ff 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -86,7 +86,7 @@ public UriResource ContextPath public UriResource Uri => ContextPath.Append(ResourceItem.PathSegment); /// - /// Constructor + /// Initializes a new instance of the class. /// /// The module context. internal ResourceContext(IModuleContext moduleContext) diff --git a/src/WebExpress.WebCore/WebResource/ResourceFile.cs b/src/WebExpress.WebCore/WebResource/ResourceFile.cs index f293aa4..1553090 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceFile.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceFile.cs @@ -19,7 +19,7 @@ public class ResourceFile : ResourceBinary public string RootDirectory { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public ResourceFile() { diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index a310efd..f17e219 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -51,7 +51,7 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent .SelectMany(x => x.ResourceContexts); /// - /// Constructor + /// Initializes a new instance of the class. /// internal ResourceManager() { diff --git a/src/WebExpress.WebCore/WebResource/ResourceRest.cs b/src/WebExpress.WebCore/WebResource/ResourceRest.cs index c0051dd..8749b66 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceRest.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceRest.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebResource public abstract class ResourceRest : Resource { /// - /// Constructor + /// Initializes a new instance of the class. /// public ResourceRest() { diff --git a/src/WebExpress.WebCore/WebSession/Session.cs b/src/WebExpress.WebCore/WebSession/Session.cs index 8cfd1bd..e3e1676 100644 --- a/src/WebExpress.WebCore/WebSession/Session.cs +++ b/src/WebExpress.WebCore/WebSession/Session.cs @@ -31,7 +31,7 @@ public class Session public Dictionary Properties { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public Session() : this(Guid.NewGuid()) @@ -39,7 +39,7 @@ public Session() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The session id. public Session(Guid id) diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 587a9d8..c121598 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -21,7 +21,7 @@ public class SessionManager : IComponent, ISystemComponent private SessionDictionary Dictionary { get; } = new SessionDictionary(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal SessionManager() { diff --git a/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs b/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs index dd8d0de..7f2f965 100644 --- a/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs +++ b/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs @@ -11,7 +11,7 @@ public class SessionPropertyParameter : SessionProperty public Dictionary Params { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public SessionPropertyParameter() { @@ -19,7 +19,7 @@ public SessionPropertyParameter() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The parameters public SessionPropertyParameter(Dictionary param) diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index d57817e..8d321ea 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -64,7 +64,7 @@ public class SearchResult public UriResource Uri { get; internal set; } /// - /// Constructor + /// Initializes a new instance of the class. /// internal SearchResult() { diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index d931234..92c2ca4 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -30,7 +30,7 @@ public sealed class SitemapManager : IComponent, ISystemComponent private SitemapNode SiteMap { get; set; } = new SitemapNode(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal SitemapManager() { diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs index 34ab389..b8852f4 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs @@ -114,7 +114,7 @@ public ICollection Path } /// - /// Constructor + /// Initializes a new instance of the class. /// public SitemapNode() { diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs new file mode 100644 index 0000000..b3fbf35 --- /dev/null +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs @@ -0,0 +1,28 @@ +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebStatusPage +{ + public interface IStatusPageContext + { + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns or sets the status code. + /// + int Code { get; } + + /// + /// Returns or sets the status title. + /// + string Title { get; } + + /// + /// Returns or sets the status icon. + /// + UriResource Icon { get; } + } +} diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs new file mode 100644 index 0000000..7e36d6b --- /dev/null +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs @@ -0,0 +1,28 @@ +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebStatusPage +{ + public class StatusPageContext : IStatusPageContext + { + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns or sets the status code. + /// + public int Code { get; internal set; } + + /// + /// Returns or sets the status title. + /// + public string Title { get; internal set; } + + /// + /// Returns or sets the status icon. + /// + public UriResource Icon { get; internal set; } + } +} diff --git a/src/WebExpress.WebCore/WebStatusPage/ResponseDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs similarity index 71% rename from src/WebExpress.WebCore/WebStatusPage/ResponseDictionary.cs rename to src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs index 498a6db..9532a9a 100644 --- a/src/WebExpress.WebCore/WebStatusPage/ResponseDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebStatusPage /// key = plugin context /// value = ResponseDictionaryItem /// - public class ResponseDictionary : Dictionary + public class StatusPageDictionary : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebStatusPage/ResponseDictionaryItem.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs similarity index 71% rename from src/WebExpress.WebCore/WebStatusPage/ResponseDictionaryItem.cs rename to src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs index 680b3e2..d17c692 100644 --- a/src/WebExpress.WebCore/WebStatusPage/ResponseDictionaryItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebStatusPage /// key = statuscode /// value = status page item /// - public class ResponseDictionaryItem : Dictionary + public class StatusPageDictionaryItem : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebStatusPage/ResponseItem.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs similarity index 62% rename from src/WebExpress.WebCore/WebStatusPage/ResponseItem.cs rename to src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs index 5643d56..0a24af1 100644 --- a/src/WebExpress.WebCore/WebStatusPage/ResponseItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs @@ -1,9 +1,10 @@ using System; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { - public class ResponseItem + public class StatusPageItem { /// /// Returns the associated plugin context. @@ -21,13 +22,23 @@ public class ResponseItem public int StatusCode { get; internal set; } /// - /// Returns or sets the type of status page. + /// Returns or sets the status title. /// - public Type StatusPageClass { get; internal set; } + public string Title { get; internal set; } + + /// + /// Returns or sets the status icon. + /// + public UriResource Icon { get; internal set; } /// - /// Returns or sets the module id. + /// Returns or sets the type of status page. /// - public string ModuleId { get; internal set; } + public Type StatusPageClass { get; internal set; } + + ///// + ///// Returns or sets the module id. + ///// + //public string ModuleId { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/ResponseManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs similarity index 79% rename from src/WebExpress.WebCore/WebStatusPage/ResponseManager.cs rename to src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index fc76557..7e03870 100644 --- a/src/WebExpress.WebCore/WebStatusPage/ResponseManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -6,13 +6,14 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { /// /// Management of status pages. /// - public class ResponseManager : IComponentPlugin, ISystemComponent + public class StatusPageManager : IComponentPlugin, ISystemComponent { /// /// An event that fires when an status page is added. @@ -32,17 +33,31 @@ public class ResponseManager : IComponentPlugin, ISystemComponent /// /// Returns the directory where the status pages are listed. /// - private ResponseDictionary Dictionary { get; } = new ResponseDictionary(); + private StatusPageDictionary Dictionary { get; } = new StatusPageDictionary(); /// /// Returns the default Items. /// - private ResponseDictionaryItem Defaults { get; } = new ResponseDictionaryItem(); + private StatusPageDictionaryItem Defaults { get; } = new StatusPageDictionaryItem(); /// - /// Constructor + /// Returns all status pages. /// - internal ResponseManager() + public IEnumerable StatusPages => Dictionary.Values + .SelectMany(x => x.Values) + .Select(x => new StatusPageContext() + { + PluginContext = x.PluginContext, + Code = x.StatusCode, + Title = x.Title, + Icon = x.Icon + } + ); + + /// + /// Initializes a new instance of the class. + /// + internal StatusPageManager() { ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { @@ -65,7 +80,7 @@ public void Initialization(IHttpServerContext context) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:responsemanager.initialization") + InternationalizationManager.I18N("webexpress:statuspagemanager.initialization") ); } @@ -83,7 +98,9 @@ public void Register(IPluginContext pluginContext) { var id = resource.Name?.ToLower(); var statusCode = -1; - var moduleId = string.Empty; + var icon = string.Empty; + var title = resource.Name; + //var moduleId = string.Empty; var defaultItem = false; foreach (var customAttribute in resource.CustomAttributes @@ -93,10 +110,18 @@ public void Register(IPluginContext pluginContext) { statusCode = Convert.ToInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); } - else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) + else if (customAttribute.AttributeType == typeof(TitleAttribute)) + { + title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(IconAttribute)) { - moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } + //else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) + //{ + // moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + //} } foreach (var customAttribute in resource.CustomAttributes @@ -112,27 +137,29 @@ public void Register(IPluginContext pluginContext) { if (!Dictionary.ContainsKey(pluginContext)) { - Dictionary.Add(pluginContext, new ResponseDictionaryItem()); + Dictionary.Add(pluginContext, new StatusPageDictionaryItem()); } var item = Dictionary[pluginContext]; if (!item.ContainsKey(statusCode)) { - item.Add(statusCode, new ResponseItem() + item.Add(statusCode, new StatusPageItem() { Id = id, StatusCode = statusCode, StatusPageClass = resource, PluginContext = pluginContext, - ModuleId = moduleId + Title = title, + Icon = new UriResource(icon), + //ModuleId = moduleId }); HttpServerContext.Log.Debug ( InternationalizationManager.I18N ( - "webexpress:responsemanager.register", + "webexpress:statuspagemanager.register", statusCode, - moduleId, + //moduleId, resource.Name ) ); @@ -143,9 +170,9 @@ public void Register(IPluginContext pluginContext) ( InternationalizationManager.I18N ( - "webexpress:responsemanager.duplicat", + "webexpress:statuspagemanager.duplicat", statusCode, - moduleId, + //moduleId, resource.Name ) ); @@ -154,24 +181,24 @@ public void Register(IPluginContext pluginContext) // default if (!Defaults.ContainsKey(statusCode)) { - Defaults.Add(statusCode, new ResponseItem() + Defaults.Add(statusCode, new StatusPageItem() { Id = id, StatusCode = statusCode, StatusPageClass = resource, PluginContext = pluginContext, - ModuleId = moduleId + //ModuleId = moduleId }); } else if (defaultItem) { - Defaults[statusCode] = new ResponseItem() + Defaults[statusCode] = new StatusPageItem() { Id = id, StatusCode = statusCode, StatusPageClass = resource, PluginContext = pluginContext, - ModuleId = moduleId + //ModuleId = moduleId }; } @@ -182,8 +209,8 @@ public void Register(IPluginContext pluginContext) ( InternationalizationManager.I18N ( - "webexpress:responsemanager.statuscode", - moduleId, + "webexpress:statuspagemanager.statuscode", + //moduleId, resource.Name ) ); @@ -228,7 +255,7 @@ internal IEnumerable GetStatusCodes(IPluginContext pluginContext) /// /// The status code. /// The first status page found to the given states or null. - private ResponseItem GetStatusPage(int status) + private StatusPageItem GetStatusPage(int status) { if (Defaults == null) { @@ -249,7 +276,7 @@ private ResponseItem GetStatusPage(int status) /// The status code. /// The plugin context where the status pages are located. /// The first status page found to the given states or null. - private ResponseItem GetStatusPage(int status, IPluginContext pluginContext) + private StatusPageItem GetStatusPage(int status, IPluginContext pluginContext) { if (pluginContext == null) { @@ -336,7 +363,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in string.Empty.PadRight(4) + InternationalizationManager.I18N ( - "webexpress:responsemanager.statuspage", + "webexpress:statuspagemanager.statuspage", statusCode ) ); diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index f46ed1f..483d4f9 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -22,7 +22,7 @@ public class TaskManager : IComponent, ISystemComponent private TaskDictionary Dictionary { get; } = new TaskDictionary(); /// - /// Constructor + /// Initializes a new instance of the class. /// internal TaskManager() { diff --git a/src/WebExpress.WebCore/WebUri/UriAuthority.cs b/src/WebExpress.WebCore/WebUri/UriAuthority.cs index 3afb5b8..8fdb5eb 100644 --- a/src/WebExpress.WebCore/WebUri/UriAuthority.cs +++ b/src/WebExpress.WebCore/WebUri/UriAuthority.cs @@ -31,7 +31,7 @@ public class UriAuthority public int? Port { get; set; } /// - /// Constructor + /// Initializes a new instance of the class. /// public UriAuthority() { @@ -39,7 +39,7 @@ public UriAuthority() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The host. public UriAuthority(string host) @@ -48,7 +48,7 @@ public UriAuthority(string host) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The host. /// The port. diff --git a/src/WebExpress.WebCore/WebUri/UriFragment.cs b/src/WebExpress.WebCore/WebUri/UriFragment.cs index f3325b5..76df232 100644 --- a/src/WebExpress.WebCore/WebUri/UriFragment.cs +++ b/src/WebExpress.WebCore/WebUri/UriFragment.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebUri public class UriFragment : UriResource { /// - /// Constructor + /// Initializes a new instance of the class. /// public UriFragment() { diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs index cb3785b..1403606 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs @@ -35,7 +35,7 @@ public class UriPathSegmentConstant : IUriPathSegmentConstant public bool IsEmpty => string.IsNullOrWhiteSpace(Value) || Value.Equals("/"); /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The tag or null @@ -45,7 +45,7 @@ public UriPathSegmentConstant(string value, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The display text. diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs index d9ab3ed..9db21b9 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs @@ -35,7 +35,7 @@ public class UriPathSegmentRoot : IUriPathSegment public bool IsEmpty => false; /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The display text. diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs index 19755c4..e84749c 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs @@ -47,7 +47,7 @@ public abstract class UriPathSegmentVariable : IUriPathSegmentVariable public bool IsEmpty => string.IsNullOrWhiteSpace(VariableName) || VariableName.Equals("/"); /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The tag or null @@ -57,7 +57,7 @@ public UriPathSegmentVariable(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The name. /// The display text. diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs index bd1bcd4..2536c72 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebUri public class UriPathSegmentVariableDouble : UriPathSegmentVariable { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The tag or null @@ -23,7 +23,7 @@ public UriPathSegmentVariableDouble(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -39,7 +39,7 @@ public UriPathSegmentVariableDouble(string name, string display, object tag = nu } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segment to copy. public UriPathSegmentVariableDouble(UriPathSegmentVariableDouble segment) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs index eea1871..baa0f5b 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs @@ -21,7 +21,7 @@ public enum Format { Full, Simple } public Format DisplayFormat { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The tag or null @@ -31,7 +31,7 @@ public UriPathSegmentVariableGuid(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -42,7 +42,7 @@ public UriPathSegmentVariableGuid(string name, string display, object tag = null } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -57,7 +57,7 @@ public UriPathSegmentVariableGuid(string name, string display, Format displayFor } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segment to copy. public UriPathSegmentVariableGuid(UriPathSegmentVariableGuid segment) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs index 9b8bbe2..2d28a5b 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebUri public class UriPathSegmentVariableInt : UriPathSegmentVariable { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The tag or null @@ -23,7 +23,7 @@ public UriPathSegmentVariableInt(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -39,7 +39,7 @@ public UriPathSegmentVariableInt(string name, string display, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segment to copy. public UriPathSegmentVariableInt(UriPathSegmentVariableInt segment) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs index 7136903..29b4825 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebUri public class UriPathSegmentVariableString : UriPathSegmentVariable { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The tag or null @@ -23,7 +23,7 @@ public UriPathSegmentVariableString(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -39,7 +39,7 @@ public UriPathSegmentVariableString(string name, string display, object tag = nu } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segment to copy. public UriPathSegmentVariableString(UriPathSegmentVariableString segment) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs index 3f72a2d..40e2145 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebUri public class UriPathSegmentVariableUInt : UriPathSegmentVariable { /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The tag or null @@ -23,7 +23,7 @@ public UriPathSegmentVariableUInt(string name, object tag = null) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path text. /// The display text. @@ -39,7 +39,7 @@ public UriPathSegmentVariableUInt(string name, string display, object tag = null } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segment to copy. public UriPathSegmentVariableUInt(UriPathSegmentVariableUInt segment) diff --git a/src/WebExpress.WebCore/WebUri/UriQuerry.cs b/src/WebExpress.WebCore/WebUri/UriQuerry.cs index 435f3e4..598984b 100644 --- a/src/WebExpress.WebCore/WebUri/UriQuerry.cs +++ b/src/WebExpress.WebCore/WebUri/UriQuerry.cs @@ -16,7 +16,7 @@ public class UriQuerry public string Value { get; protected set; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The key. /// The value. diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriResource.cs index f1c9746..4daee92 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriResource.cs @@ -130,7 +130,7 @@ public Dictionary Parameters } /// - /// Constructor + /// Initializes a new instance of the class. /// public UriResource() { @@ -138,7 +138,7 @@ public UriResource() } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The scheme (e.g. Http, FTP). /// The authority (e.g. user@example.com:8080). @@ -151,7 +151,7 @@ public UriResource(UriScheme scheme, UriAuthority authority, string uri) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. public UriResource(string uri) @@ -207,19 +207,19 @@ public UriResource(string uri) /// The uri. public UriResource(UriResource uri) { - Scheme = uri.Scheme; - Authority = uri.Authority; - PathSegments = uri.PathSegments.Select(x => x.Copy()).ToList(); - Query = uri.Query.Select(x => new UriQuerry(x.Key, x.Value)).ToList(); - Fragment = uri.Fragment; - ServerRoot = uri.ServerRoot; - ApplicationRoot = uri.ApplicationRoot; - ModuleRoot = uri.ModuleRoot; - ResourceRoot = uri.ResourceRoot; + Scheme = uri?.Scheme ?? UriScheme.Http; + Authority = uri?.Authority; + PathSegments = uri?.PathSegments.Select(x => x.Copy()).ToList() ?? []; + Query = uri?.Query.Select(x => new UriQuerry(x.Key, x.Value)).ToList() ?? []; + Fragment = uri?.Fragment; + ServerRoot = uri?.ServerRoot; + ApplicationRoot = uri?.ApplicationRoot; + ModuleRoot = uri?.ModuleRoot; + ResourceRoot = uri?.ResourceRoot; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The path segments. public UriResource(params IUriPathSegment[] segments) @@ -233,7 +233,7 @@ public UriResource(params IUriPathSegment[] segments) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. /// The path segments. @@ -247,7 +247,7 @@ public UriResource(UriResource uri, IEnumerable segments) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The uri. /// The path segments. @@ -262,7 +262,7 @@ public UriResource(UriResource uri, IEnumerable segments, IEnum } /// - /// Constructor + /// Initializes a new instance of the class. /// /// The scheme (e.g. Http, FTP). /// The authority (e.g. user@example.com:8080). From 47fde46670777a5c27adb20b0c077b22aa0c4622 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Sep 2024 23:14:03 +0200 Subject: [PATCH 009/162] refactoring --- .../Fixture/NonParallelTestsCollection.cs | 11 + .../Fixture/UnitTestControlFixture.cs | 205 ++++++++++++++++++ .../Html/UnitTestHtmlElementFieldLabel.cs | 4 +- .../Manager/UnitTestApplication.cs | 63 ++++++ .../Manager/UnitTestPlugin.cs | 186 ++++++++++++++++ .../Message/UnitTestGetRequest.cs | 109 ++++------ .../{Request.cs => UnitTestRequest.cs} | 6 +- .../Schedule/UnitTestClock.cs | 6 +- .../Schedule/UnitTestCron.cs | 4 +- .../TestApplication.cs | 37 ++++ src/WebExpress.WebCore.Test/TestModule.cs | 40 ++++ src/WebExpress.WebCore.Test/TestPage.cs | 81 +++++++ src/WebExpress.WebCore.Test/TestPlugin.cs | 36 +++ .../Uri/UnitTestUriAbsolute.cs | 6 +- .../Uri/UnitTestUriRelative.cs | 8 +- .../Uri/UnitTestUriRelativeAppend.cs | 7 +- .../Uri/UnitTestUriRelativeExtendedPath.cs | 6 +- .../Uri/UnitTestUriRelativeSkip.cs | 7 +- .../Uri/UnitTestUriRelativeTake.cs | 7 +- .../WebExpress.WebCore.Test.csproj | 34 +++ .../WebPage/RenderContext.cs | 2 +- src/WebExpress.WebCore/WebPage/VisualTree.cs | 21 +- .../WebPlugin/PluginManager.cs | 4 +- .../WebResource/IResourceContext.cs | 9 +- .../WebResource/Resource.cs | 2 +- .../WebSitemap/SitemapManager.cs | 49 +++-- .../WebSitemap/SitemapNode.cs | 22 +- .../WebStatusPage/StatusPageManager.cs | 2 +- 28 files changed, 832 insertions(+), 142 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs create mode 100644 src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs rename src/WebExpress.WebCore.Test/Request/{Request.cs => UnitTestRequest.cs} (51%) create mode 100644 src/WebExpress.WebCore.Test/TestApplication.cs create mode 100644 src/WebExpress.WebCore.Test/TestModule.cs create mode 100644 src/WebExpress.WebCore.Test/TestPage.cs create mode 100644 src/WebExpress.WebCore.Test/TestPlugin.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs b/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs new file mode 100644 index 0000000..0013742 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs @@ -0,0 +1,11 @@ +namespace WebExpress.WebCore.Test.Fixture +{ + /// + /// Defines a collection of tests that should not be run in parallel. + /// + [CollectionDefinition("NonParallelTests", DisableParallelization = true)] + public class NonParallelTestsCollection : ICollectionFixture + { + + } +} diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs new file mode 100644 index 0000000..3f80f54 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -0,0 +1,205 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using System.Net; +using System.Reflection; +using System.Text; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test.Fixture +{ + public class UnitTestControlFixture : IDisposable + { + /// + /// Returns a guard to protect against concurrent access. + /// + private static object guard = new object(); + + /// + /// Initializes a new instance of the class and boot the component manager. + /// + public UnitTestControlFixture() + { + lock (guard) + { + var initializationComponentManager = typeof(ComponentManager).GetMethod("Initialization", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, [typeof(IHttpServerContext)]); + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + null, + new Log() { LogMode = LogMode.Off }, + null + ); + + initializationComponentManager.Invoke(null, [serverContext]); + } + } + + /// + /// Register a plugin. + /// + /// The plugin to be registered. + public void RegisterPlugin(Type plugin) + { + lock (guard) + { + var registerPluginManager = typeof(PluginManager).GetMethod("Register", BindingFlags.NonPublic | BindingFlags.Instance, [typeof(Assembly), typeof(PluginLoadContext)]); + + registerPluginManager.Invoke(ComponentManager.PluginManager, [plugin.Assembly, null]); + } + } + + /// + /// Create a fake request. + /// + /// The content. + /// A fake request for testing. + public WebMessage.Request CrerateRequest(string content = "") + { + var ctorRequestHeaderFields = typeof(RequestHeaderFields).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection)], null); + var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(IHttpServerContext), typeof(RequestHeaderFields)], null); + var featureCollection = new FeatureCollection(); + var firstLine = content.Split('\n').FirstOrDefault(); + var lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); + var pos = content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4; + var innerContent = pos < content.Length ? content.Substring(pos) : ""; + var contentBytes = Encoding.UTF8.GetBytes(innerContent); + + var requestFeature = new HttpRequestFeature + { + Headers = new HeaderDictionary + { + ["Host"] = "localhost", + ["Connection"] = "keep-alive", + ["ContentType"] = "text/html", + ["ContentLength"] = innerContent.Length.ToString(), + ["ContentLanguage"] = "en", + ["ContentEncoding"] = "gzip, deflate, br, zstd", + ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + ["AcceptEncoding"] = "gzip, deflate, br, zstd", + ["AcceptLanguage"] = "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", + ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", + ["Referer"] = "0HN50661TV8TP" + }, + Body = new MemoryStream(contentBytes), + Method = firstLine.Split(' ').FirstOrDefault(), + RawTarget = firstLine.Split(' ').Skip(1).FirstOrDefault().Split('?').FirstOrDefault(), + QueryString = "?" + firstLine.Split(' ').Skip(1).FirstOrDefault().Split('?').Skip(1).FirstOrDefault(), + }; + + foreach (var line in filteredLines) + { + var key = line.Split(':').FirstOrDefault().Trim(); + var value = line.Split(':').Skip(1).FirstOrDefault().Trim(); + requestFeature.Headers[key] = value; + } + + requestFeature.Headers.ContentLength = contentBytes.Length; + + var requestIdentifierFeature = new HttpRequestIdentifierFeature + { + TraceIdentifier = "Ihr TraceIdentifier-Wert" + }; + + var connectionFeature = new HttpConnectionFeature + { + LocalPort = 8080, + LocalIpAddress = IPAddress.Parse("192.168.0.1"), + RemotePort = 8080, + RemoteIpAddress = IPAddress.Parse("127.0.0.1"), + ConnectionId = "0HN50661TV8TP" + }; + + featureCollection.Set(requestFeature); + featureCollection.Set(requestIdentifierFeature); + featureCollection.Set(connectionFeature); + + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + "", + "", + "", + null, + null, + new Log() { LogMode = LogMode.Off }, + null + ); + var headers = (RequestHeaderFields)ctorRequestHeaderFields.Invoke([featureCollection]); + var request = (WebMessage.Request)ctorRequest.Invoke([featureCollection, serverContext, headers]); + + return request; + } + + /// + /// Create a fake render context. + /// + /// A fake context for testing. + public RenderContext CrerateContext() + { + var request = CrerateRequest(); + var page = new TestPage(); + var visualTree = new VisualTree(); + + page.Initialization(CrerateResourceContext()); + + return new RenderContext(page, request, visualTree); + } + + /// + /// Create a fake resource context. + /// + /// A fake context for testing. + public ResourceContext CrerateResourceContext() + { + var ctorResourceContext = typeof(ResourceContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); + + var moduleContext = ComponentManager.ModuleManager.Modules + .Where(x => x.ModuleId == typeof(TestModule).FullName.ToLower()) + .FirstOrDefault(); + + var resourceContext = (ResourceContext)ctorResourceContext.Invoke([moduleContext]); + + return resourceContext; + } + + /// + /// Gets the content of an embedded resource as a string. + /// + /// The name of the resource file. + /// The content of the embedded resource as a string. + public string GetEmbeddedResource(string fileName) + { + var assembly = GetType().Assembly; + var resourceName = assembly.GetManifestResourceNames() + .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); + + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (StreamReader reader = new StreamReader(stream)) + { + return reader.ReadToEnd(); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs index 48c80d4..f796872 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs @@ -95,7 +95,7 @@ public void CloseTag() } /// - /// Tests a tag. + /// Tests the class attribute. /// [Fact] public void Class() @@ -110,7 +110,7 @@ public void Class() } /// - /// Tests a tag. + /// Tests the style attribute. /// [Fact] public void Style() diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs new file mode 100644 index 0000000..d9e1637 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -0,0 +1,63 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the application manager. + /// + /// The fixture. + [Collection("NonParallelTests")] + public class UnitTestApplication(UnitTestControlFixture fixture) : IClassFixture + { + private static readonly object _lock = new object(); + + /// + /// Test the register function of the application manager. + /// + [Fact] + public void Register() + { + lock (_lock) + { + // preconditions + var i = 0; + bool triggered = false; + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + ComponentManager.ApplicationManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + ComponentManager.ApplicationManager.AddApplication += (s, e) => { i++; triggered = true; }; + + // test execution + ComponentManager.ApplicationManager.Register(plugin); + + Assert.Single(ComponentManager.ApplicationManager.Applications); + Assert.Equal("webexpress.webcore.test.testapplication", ComponentManager.ApplicationManager.Applications.FirstOrDefault().ApplicationId); + Assert.Equal(1, i); + Assert.True(triggered); + + // postconditions + ComponentManager.ApplicationManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the remove function of the application manager. + /// + [Fact] + public void Remove() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + ComponentManager.ApplicationManager.Remove(plugin); + + Assert.Empty(ComponentManager.ApplicationManager.Applications); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs new file mode 100644 index 0000000..597941e --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -0,0 +1,186 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the plugin manager. + /// + /// The fixture. + [Collection("NonParallelTests")] + public class UnitTestPlugin(UnitTestControlFixture fixture) : IClassFixture + { + private static readonly object _lock = new object(); + + /// + /// Test the register function of the plugin manager. + /// + [Fact] + public void Register() + { + lock (_lock) + { + // test execution + ComponentManager.PluginManager.Register(); + + Assert.Single(ComponentManager.PluginManager.Plugins); + Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the register function of the plugin manager. + /// + [Fact] + public void RegisterAssembly() + { + lock (_lock) + { + // test execution + fixture.RegisterPlugin(typeof(TestPlugin)); + + Assert.Single(ComponentManager.PluginManager.Plugins); + Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the event of the plugin manager. + /// + [Fact] + public void RegisterEvent() + { + lock (_lock) + { + // preconditions + var i = 0; + bool triggered = false; + ComponentManager.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; + + // test execution + fixture.RegisterPlugin(typeof(TestPlugin)); + + Assert.Single(ComponentManager.PluginManager.Plugins); + Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); + Assert.Equal(1, i); + Assert.True(triggered); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the remove function of the plugin manager. + /// + [Fact] + public void Remove() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + ComponentManager.PluginManager.Remove(plugin); + + Assert.Empty(ComponentManager.PluginManager.Plugins); + } + } + + /// + /// Test the event of the plugin manager. + /// + [Fact] + public void RemoveEvent() + { + lock (_lock) + { + // preconditions + var i = 1; + ComponentManager.PluginManager.RemovePlugin += (s, e) => i--; + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + ComponentManager.PluginManager.Remove(plugin); + + Assert.Empty(ComponentManager.PluginManager.Plugins); + Assert.Equal(0, i); + } + } + + /// + /// Test the get plugin function of the plugin manager. + /// + [Fact] + public void GetPlugin() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin1 = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + var plugin2 = ComponentManager.PluginManager.GetPlugin(plugin1.PluginId); + + Assert.Equal(plugin1, plugin2); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the event of the plugin manager. + /// + [Fact] + public void Boot() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + ComponentManager.PluginManager.Boot(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + + Assert.Single(ComponentManager.PluginManager.Plugins); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + + /// + /// Test the event of the plugin manager. + /// + [Fact] + public void ShutDown() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + ComponentManager.PluginManager.ShutDown(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + + Assert.Single(ComponentManager.PluginManager.Plugins); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs index dd6252a..2440f2e 100644 --- a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs +++ b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs @@ -1,92 +1,77 @@ -using System.IO; -using System.Net.Http; -using Xunit; +using WebExpress.WebCore.Test.Fixture; namespace WebExpress.WebCore.Test.Message { - public class UnitTestGetRequest + /// + /// UnitTestGetRequest class for testing HTTP GET requests. + /// + /// The fixture used for setting up the tests. + public class UnitTestGetRequest(UnitTestControlFixture fixture) : IClassFixture { + /// + /// Tests a general GET request. + /// [Fact] - public void Get_General() + public void General() { - var client = new HttpClient(); - //client. + var content = fixture.GetEmbeddedResource("general.get"); + var request = fixture.CrerateRequest(content); - //client.Headers.Add("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)"); - - //Stream data = client.OpenRead("http://localhost/"); - //StreamReader reader = new StreamReader(data); - //string s = reader.ReadToEnd(); - //Console.WriteLine(s); - //data.Close(); - //reader.Close(); - - //using var reader = new BinaryReader(new FileStream(Path.Combine("test", "general.get"), FileMode.Open)); - //var request = Request.Create(reader, "127.0.0.1"); - - //Assert.True - //( - // request.Uri?.ToString() == "/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", - // "Fehler in der Funktion Get_General" - //); + Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } + /// + /// Tests a GET request with less data. + /// [Fact] - public void Get_Less() + public void Less() { - //using var reader = new BinaryReader(new FileStream(Path.Combine("test", "less.get"), FileMode.Open)); - //var request = Request.Create(reader, "127.0.0.1"); + var content = fixture.GetEmbeddedResource("less.get"); + var request = fixture.CrerateRequest(content); - //Assert.True - //( - // request.Uri?.ToString() == "/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", - // "Fehler in der Funktion Get_Less" - //); + Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } + /// + /// Tests a GET request with massive data. + /// [Fact] - public void Get_Massive() + public void Massive() { - //using var reader = new BinaryReader(new FileStream(Path.Combine("test", "massive.get"), FileMode.Open)); - //var request = Request.Create(reader, "127.0.0.1"); + var content = fixture.GetEmbeddedResource("massive.get"); + var request = fixture.CrerateRequest(content); - //Assert.True - //( - // request.Uri?.ToString() == "/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", - // "Fehler in der Funktion Get_Massive" - //); + Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } + /// + /// Tests a GET request with parameters. + /// [Fact] - public void Get_Param() + public void GetParameter() { - //using var reader = new BinaryReader(new FileStream(Path.Combine("test", "param.get"), FileMode.Open)); - //var request = Request.Create(reader, "127.0.0.1"); - //var param = request?.GetParameter("a")?.Value; + var content = fixture.GetEmbeddedResource("param.get"); + var request = fixture.CrerateRequest(content); + var param = request?.GetParameter("a")?.Value; - //Assert.True - //( - // request.Uri?.ToString() == "/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A" && - // param != null && param == "1", - // "Fehler in der Funktion Get_Param" - //); + Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); + Assert.Equal("1", param); } + /// + /// Tests a GET request with parameters containing umlauts. + /// [Fact] - public void Get_Param_Umlaut() + public void GetParameterWithUmlaut() { - //using var reader = new BinaryReader(new FileStream(Path.Combine("test", "param_umlaut.get"), FileMode.Open)); - //var request = Request.Create(reader, "127.0.0.1"); - //var a = request?.GetParameter("a")?.Value; - //var b = request?.GetParameter("b")?.Value; + var content = fixture.GetEmbeddedResource("param_umlaut.get"); + var request = fixture.CrerateRequest(content); + var a = request?.GetParameter("a")?.Value; + var b = request?.GetParameter("b")?.Value; - //Assert.True - //( - // request.Uri?.ToString() == "/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A" && - // a != null && a == "ä" && - // b != null && b == "ö ü", - // "Fehler in der Funktion Get_Param_Umlaut" - //); + Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); + Assert.Equal("ä", a); + Assert.Equal("ö ü", b); } } } diff --git a/src/WebExpress.WebCore.Test/Request/Request.cs b/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs similarity index 51% rename from src/WebExpress.WebCore.Test/Request/Request.cs rename to src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs index c9b81fc..cff81c1 100644 --- a/src/WebExpress.WebCore.Test/Request/Request.cs +++ b/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs @@ -1,9 +1,9 @@ -using Xunit; +using WebExpress.WebCore.Test.Fixture; namespace WebExpress.WebCore.Test.Request { - - public class Request + [Collection("NonParallelTests")] + public class UnitTestRequest(UnitTestControlFixture fixture) : IClassFixture { [Fact] diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs index 0b04f10..b06f198 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs @@ -1,11 +1,13 @@ -using WebExpress.WebCore.WebJob; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebJob; namespace WebExpress.WebCore.Test.Schedule { /// /// Tests the scheduler's clock. /// - public class UnitTestClock + [Collection("NonParallelTests")] + public class UnitTestClock(UnitTestControlFixture fixture) : IClassFixture { [Fact] public void Synchronize_1() diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs index c041292..aa7a247 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs @@ -1,4 +1,5 @@ using System.Globalization; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; @@ -8,7 +9,8 @@ namespace WebExpress.WebCore.Test.Schedule /// /// Test the cron job of the scheduler. /// - public class UnitTestCron + [Collection("NonParallelTests")] + public class UnitTestCron(UnitTestControlFixture fixture) : IClassFixture { [Fact] public void Create_1() diff --git a/src/WebExpress.WebCore.Test/TestApplication.cs b/src/WebExpress.WebCore.Test/TestApplication.cs new file mode 100644 index 0000000..f16ca71 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestApplication.cs @@ -0,0 +1,37 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy application for testing purposes. + /// + [Name("webexpress.webui.unittest")] + [Description("plugin.description")] + [Icon("/assets/img/Logo.png")] + [Dependency("webexpress.webui")] + public sealed class TestApplication : IApplication + { + /// + /// Initialization of the application. + /// + /// The application context. + public void Initialization(IApplicationContext applicationContext) + { + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModule.cs b/src/WebExpress.WebCore.Test/TestModule.cs new file mode 100644 index 0000000..7980556 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModule.cs @@ -0,0 +1,40 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + [Name("module.name")] + [Description("module.description")] + [Icon("/assets/img/Logo.png")] + [AssetPath("/")] + [ContextPath("/")] + public sealed class TestModule : IModule + { + /// + /// Initialization of the module. + /// + /// The module context. + public void Initialization(IModuleContext moduleContext) + { + throw new NotImplementedException(); + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPage.cs b/src/WebExpress.WebCore.Test/TestPage.cs new file mode 100644 index 0000000..8f76b6f --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestPage.cs @@ -0,0 +1,81 @@ +using System.Globalization; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Segment(null, "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestPage : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the resource context. + /// + public IResourceContext ResourceContext { get; private set; } + + /// + /// Returns or sets the culture information. + /// + public CultureInfo Culture { get => CultureInfo.CurrentCulture; set => throw new NotImplementedException(); } + + /// + /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// + /// The context of the resource. + public void Initialization(IResourceContext resourceContext) + { + ResourceContext = resourceContext; + } + + /// + /// Post-processes the request and response. + /// + /// The request. + /// The response. + /// The processed response. + public Response PostProcess(WebMessage.Request request, Response response) + { + return null; + } + + /// + /// Pre-processes the request. + /// + /// The request. + public void PreProcess(WebMessage.Request request) + { + + } + + /// + /// Processes the request. + /// + /// The request. + /// The processed response. + public Response Process(WebMessage.Request request) + { + return null; + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs new file mode 100644 index 0000000..be4c5e0 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -0,0 +1,36 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy plugin for testing purposes. + /// + [Name("webexpress.webui.unittest")] + [Description("plugin.description")] + [Icon("/assets/img/Logo.png")] + public sealed class TestPlugin : IPlugin + { + /// + /// Initialization of the plugin. + /// + /// The plugin context. + public void Initialization(IPluginContext pluginContext) + { + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs index a001e5b..0e51997 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs @@ -1,11 +1,13 @@ -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { /// /// Tests an absolute Uri. /// - public class UnitTestUriAbsolute + [Collection("NonParallelTests")] + public class UnitTestUriAbsolute(UnitTestControlFixture fixture) : IClassFixture { [Fact] public void Test_0() diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs index fdc2ee6..23f2a36 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs @@ -1,13 +1,13 @@ -using System.Linq; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebUri; -using Xunit; namespace WebExpress.WebCore.Test.Uri { /// - /// Tests an relative Uri. + /// Tests an relative uri. /// - public class UnitTestUriRelative + [Collection("NonParallelTests")] + public class UnitTestUriRelative(UnitTestControlFixture fixture) : IClassFixture { [Fact] public void Test_0() diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs index 1dc41bc..5cda0bb 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs @@ -1,12 +1,13 @@ -using WebExpress.WebCore.WebUri; -using Xunit; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { /// /// Tests the append method. /// - public class UnitTestUriRelativeAppend + [Collection("NonParallelTests")] + public class UnitTestUriRelativeAppend(UnitTestControlFixture fixture) : IClassFixture { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs index 843c0f3..28e5bf8 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs @@ -1,13 +1,13 @@ -using System.Collections.Generic; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebUri; -using Xunit; namespace WebExpress.WebCore.Test.Uri { /// /// Tests the extended path property. /// - public class UnitTestUriRelativeExtendedPath + [Collection("NonParallelTests")] + public class UnitTestUriRelativeExtendedPath(UnitTestControlFixture fixture) : IClassFixture { private readonly UriResource Uri = new UriResource("http://user@example.com:80"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs index c70f292..e88b631 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs @@ -1,12 +1,13 @@ -using WebExpress.WebCore.WebUri; -using Xunit; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { /// /// Tests the skip method. /// - public class UnitTestUriRelativeSkip + [Collection("NonParallelTests")] + public class UnitTestUriRelativeSkip(UnitTestControlFixture fixture) : IClassFixture { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs index fd56061..cf98102 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs @@ -1,12 +1,13 @@ -using WebExpress.WebCore.WebUri; -using Xunit; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { /// /// Tests the take method. /// - public class UnitTestUriRelativeTake + [Collection("NonParallelTests")] + public class UnitTestUriRelativeTake(UnitTestControlFixture fixture) : IClassFixture { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index fc0342e..d053309 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -9,6 +9,40 @@ true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 50853c5..329476a 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -36,7 +36,7 @@ public class RenderContext /// /// Provides the context of the associated application. /// - public IApplicationContext ApplicationContext => Page?.ResourceContext?.ApplicationContext; + public IApplicationContext ApplicationContext => Page?.ResourceContext?.ModuleContext?.ApplicationContext; /// /// Returns the contents of a page. diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index 78b43a0..af1026e 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -2,39 +2,38 @@ using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebHtml; -using WebExpress.WebCore.WebPage; -namespace WebExpress.WebCore.WebResource +namespace WebExpress.WebCore.WebPage { /// /// The content of a page is determined by the visual tree. /// - public abstract class VisualTree : IVisualTree + public class VisualTree : IVisualTree { /// /// Returns the favicons. /// - public List Favicons { get; } = new List(); + public List Favicons { get; } = []; /// /// Returns the internal stylesheet. /// - public List Styles { get; } = new List(); + public List Styles { get; } = []; /// /// Returns the links to the java script files to be used, which are inserted in the header. /// - public List HeaderScriptLinks { get; } = new List(); + public List HeaderScriptLinks { get; } = []; /// /// Returns the links to the java script files to be used. /// - public List ScriptLinks { get; } = new List(); + public List ScriptLinks { get; } = []; /// /// Returns the links to the java script files to be used, which are inserted in the header. /// - public List HeaderScripts { get; } = new List(); + public List HeaderScripts { get; } = []; /// /// Returns the links to the java script files to be used. @@ -44,12 +43,12 @@ public abstract class VisualTree : IVisualTree /// /// Returns the links to the css files to be used. /// - public List CssLinks { get; } = new List(); + public List CssLinks { get; } = []; /// /// Returns the meta information. /// - public List> Meta { get; } = new List>(); + public List> Meta { get; } = []; /// /// Returns the content. @@ -118,7 +117,7 @@ public virtual IHtmlNode Render(RenderContext context) html.Head.Meta = Meta; html.Head.Scripts = HeaderScripts; html.Body.Elements.Add(Content); - html.Body.Scripts = Scripts.Values.ToList(); + html.Body.Scripts = [.. Scripts.Values]; html.Head.CssLinks = CssLinks.Where(x => x != null).Select(x => x.ToString()); html.Head.ScriptLinks = HeaderScriptLinks?.Where(x => x != null).Select(x => x.ToString()); diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 43019d2..04d52fe 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -189,7 +189,7 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext .Where(x => x.IsClass && x.IsSealed) .Where(x => x.GetInterface(typeof(IPlugin).Name) != null)) { - var id = type.Namespace?.ToLower(); + var id = $"{type.Namespace?.ToLower()}.{type.Name?.ToLower()}"; var name = type.Assembly.GetCustomAttribute()?.Title; var icon = string.Empty; var description = type.Assembly.GetCustomAttribute()?.Description; @@ -292,6 +292,8 @@ public void Remove(IPluginContext pluginContext) var pluginItem = GetPluginItem(pluginContext); pluginItem?.PluginLoadContext?.Unload(); + + Dictionary.Remove(pluginContext.PluginId); } /// diff --git a/src/WebExpress.WebCore/WebResource/IResourceContext.cs b/src/WebExpress.WebCore/WebResource/IResourceContext.cs index a593cbd..e0f7654 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceContext.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -14,10 +13,10 @@ public interface IResourceContext /// IPluginContext PluginContext { get; } - /// - /// Returns the associated application context. - /// - IApplicationContext ApplicationContext { get; } + ///// + ///// Returns the associated application context. + ///// + //IApplicationContext ApplicationContext { get; } /// /// Returns the corresponding module context. diff --git a/src/WebExpress.WebCore/WebResource/Resource.cs b/src/WebExpress.WebCore/WebResource/Resource.cs index fb0895f..8c7a2cc 100644 --- a/src/WebExpress.WebCore/WebResource/Resource.cs +++ b/src/WebExpress.WebCore/WebResource/Resource.cs @@ -15,7 +15,7 @@ public abstract class Resource : IResource /// /// Returns the context of the application. /// - public IApplicationContext ApplicationContext => ResourceContext?.ApplicationContext; + public IApplicationContext ApplicationContext => ResourceContext?.ModuleContext?.ApplicationContext; /// /// Returns the context of the module. diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 92c2ca4..e0e2032 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -27,7 +27,12 @@ public sealed class SitemapManager : IComponent, ISystemComponent /// /// Returns the side map. /// - private SitemapNode SiteMap { get; set; } = new SitemapNode(); + private SitemapNode Root { get; set; } = new SitemapNode(); + + /// + /// Returns the side map. + /// + public IEnumerable SiteMap => Root.GetPreOrder().Select(x => x.ResourceContext); /// /// Initializes a new instance of the class. @@ -120,7 +125,7 @@ public void Refresh() )); } - SiteMap = newSiteMapNode; + Root = newSiteMapNode; using (var frame = new LogFrameSimple(HttpServerContext.Log)) { @@ -141,7 +146,7 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) var variables = new Dictionary(); var result = SearchNode ( - SiteMap, + Root, new Queue(requestUri.Segments.Select(x => (x == "/" ? x : (x.EndsWith("/") ? x[..^1] : x)))), new Queue(), searchContext @@ -167,7 +172,7 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) /// Returns the uri taking into account the context or null. public UriResource GetUri(params Parameter[] parameters) where T : IResource { - var node = SiteMap.GetPreOrder() + var node = Root.GetPreOrder() .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) .FirstOrDefault(); @@ -182,9 +187,9 @@ public UriResource GetUri(params Parameter[] parameters) where T : IResource /// Returns the uri taking into account the context or null. public UriResource GetUri(IModuleContext moduleContext) where T : IResource { - var node = SiteMap.GetPreOrder() + var node = Root.GetPreOrder() .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) - .Where(x => x.ModuleContext == moduleContext) + .Where(x => x.ResourceContext.ModuleContext == moduleContext) .FirstOrDefault(); return node?.ResourceContext?.Uri; @@ -198,9 +203,9 @@ public UriResource GetUri(IModuleContext moduleContext) where T : IResource /// Returns the uri taking into account the context or null. public UriResource GetUri(IResourceContext resourceContext) where T : IResource { - var node = SiteMap.GetPreOrder() + var node = Root.GetPreOrder() .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) - .Where(x => x.ModuleContext == resourceContext.ModuleContext) + .Where(x => x.ResourceContext.ModuleContext == resourceContext.ModuleContext) .FirstOrDefault(); return node?.ResourceContext?.Uri; @@ -227,7 +232,7 @@ private static SitemapNode CreateSiteMap { PathSegment = pathSegment as IUriPathSegment, Parent = parent, - ApplicationContext = applicationContext + //ApplicationContext = applicationContext }; if (contextPathSegments.Any()) @@ -259,8 +264,8 @@ private static SitemapNode CreateSiteMap { PathSegment = pathSegment as IUriPathSegment, Parent = parent, - ApplicationContext = moduleContext?.ApplicationContext, - ModuleContext = moduleContext + //ApplicationContext = moduleContext?.ApplicationContext, + //ModuleContext = moduleContext }; if (contextPathSegments.Any()) @@ -295,8 +300,8 @@ private static SitemapNode CreateSiteMap PathSegment = pathSegment as IUriPathSegment, Parent = parent, ResourceItem = !contextPathSegments.Any() ? resourceItem : null, - ApplicationContext = resourceContext?.ModuleContext?.ApplicationContext, - ModuleContext = resourceContext?.ModuleContext, + //ApplicationContext = resourceContext?.ModuleContext?.ApplicationContext, + //ModuleContext = resourceContext?.ModuleContext, ResourceContext = resourceContext }; @@ -324,8 +329,8 @@ private void MergeSitemap(SitemapNode first, SitemapNode second) if (fc.ResourceItem == null) { fc.ResourceItem = sc.ResourceItem; - fc.ApplicationContext = sc.ApplicationContext; - fc.ModuleContext = sc.ModuleContext; + //fc.ApplicationContext = sc.ApplicationContext; + //fc.ModuleContext = sc.ModuleContext; fc.ResourceContext = sc.ResourceContext; fc.Instance = sc.Instance; fc.Parent = sc.Parent; @@ -377,8 +382,8 @@ SearchContext searchContext { Id = node.ResourceItem.ResourceId, Title = node.ResourceItem.Title, - ApplicationContext = node.ApplicationContext, - ModuleContext = node.ModuleContext, + //ApplicationContext = node.ApplicationContext, + //ModuleContext = node.ModuleContext, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()), @@ -391,8 +396,8 @@ SearchContext searchContext { Id = node.ResourceItem.ResourceId, Title = node.ResourceItem.Title, - ApplicationContext = node.ApplicationContext, - ModuleContext = node.ModuleContext, + //ApplicationContext = node.ApplicationContext, + //ModuleContext = node.ModuleContext, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()), @@ -409,8 +414,8 @@ SearchContext searchContext // 404 return new SearchResult() { - ApplicationContext = node.ApplicationContext, - ModuleContext = node.ModuleContext, + //ApplicationContext = node.ApplicationContext, + //ModuleContext = node.ModuleContext, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()) @@ -497,7 +502,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ) ); - var preorder = SiteMap + var preorder = Root .GetPreOrder() .Select(x => InternationalizationManager.I18N ( diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs index b8852f4..042ff53 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; @@ -22,15 +20,15 @@ public class SitemapNode /// public ResourceItem ResourceItem { get; internal set; } - /// - /// Returns the context of the application. - /// - public IApplicationContext ApplicationContext { get; internal set; } + ///// + ///// Returns the context of the application. + ///// + //public IApplicationContext ApplicationContext { get; internal set; } - /// - /// Returns the context of the module. - /// - public IModuleContext ModuleContext { get; internal set; } + ///// + ///// Returns the context of the module. + ///// + //public IModuleContext ModuleContext { get; internal set; } /// /// Returns the context of the resource. @@ -150,8 +148,8 @@ public SitemapNode Copy() { PathSegment = PathSegment, ResourceItem = ResourceItem, - ApplicationContext = ApplicationContext, - ModuleContext = ModuleContext, + //ApplicationContext = ApplicationContext, + //ModuleContext = ModuleContext, ResourceContext = ResourceContext, Instance = Instance, Parent = Parent, diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 7e03870..c500b2d 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -251,7 +251,7 @@ internal IEnumerable GetStatusCodes(IPluginContext pluginContext) } /// - /// Returns the default class for an status page. + /// Returns the class for an status code. /// /// The status code. /// The first status page found to the given states or null. From 7b0a9edd4627e976aa373cc7a5019f84e32b2925 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 23 Sep 2024 21:56:21 +0200 Subject: [PATCH 010/162] added new get method --- .../WebPlugin/PluginManager.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 04d52fe..eb56d8d 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -383,6 +383,23 @@ public IPluginContext GetPlugin(string pluginId) .FirstOrDefault(); } + /// + /// Returns a plugin context based on its id. + /// + /// The type of the plugin. + /// The plugin context. + public IPluginContext GetPlugin(Type plugin) + { + return Dictionary.Values + .Where + ( + x => x.PluginContext != null && + x.PluginClass.Equals(plugin) + ) + .Select(x => x.PluginContext) + .FirstOrDefault(); + } + /// /// Returns a plugin item based on the context. /// From 3febb1c8c1f342185cfe42b5dd8e2b3c9fe6fc6b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 23 Sep 2024 21:57:31 +0200 Subject: [PATCH 011/162] added new internationalization tests --- .../Fixture/UnitTestControlFixture.cs | 3 +- .../Internationalization/de | 4 ++ .../Internationalization/en | 3 ++ .../Manager/UnitTestInternationalization.cs | 54 +++++++++++++++++++ .../Manager/UnitTestPlugin.cs | 24 ++++++++- src/WebExpress.WebCore.Test/TestPlugin.cs | 2 +- .../WebExpress.WebCore.Test.csproj | 4 ++ 7 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Internationalization/de create mode 100644 src/WebExpress.WebCore.Test/Internationalization/en create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 3f80f54..c0cba4f 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using System.Globalization; using System.Net; using System.Reflection; using System.Text; @@ -37,7 +38,7 @@ public UnitTestControlFixture() Environment.CurrentDirectory, Environment.CurrentDirectory, null, - null, + CultureInfo.GetCultureInfo("en"), new Log() { LogMode = LogMode.Off }, null ); diff --git a/src/WebExpress.WebCore.Test/Internationalization/de b/src/WebExpress.WebCore.Test/Internationalization/de new file mode 100644 index 0000000..fb13f3f --- /dev/null +++ b/src/WebExpress.WebCore.Test/Internationalization/de @@ -0,0 +1,4 @@ +# Deutsch + +unit.test.message=Dies ist ein Test + diff --git a/src/WebExpress.WebCore.Test/Internationalization/en b/src/WebExpress.WebCore.Test/Internationalization/en new file mode 100644 index 0000000..8636146 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Internationalization/en @@ -0,0 +1,3 @@ +# Englisch + +unit.test.message=This is a test \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs new file mode 100644 index 0000000..03a4963 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -0,0 +1,54 @@ +using System.Globalization; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the internationalization manager. + /// + /// The fixture. + [Collection("NonParallelTests")] + public class UnitTestInternationalization(UnitTestControlFixture fixture) : IClassFixture + { + private static readonly object _lock = new object(); + + /// + /// Test the I18N function of the plugin manager. + /// + [Theory] + [InlineData("webexpress.webcore.test.testplugin:unit.test.message", "Dies ist ein Test", "de")] + [InlineData("webexpress.webcore.test.testplugin:unit.test.message", "This is a test", null, typeof(TestPlugin))] + [InlineData("unit.test.message", "Dies ist ein Test", "de", typeof(TestPlugin))] + [InlineData("unit.test.message", "Dies ist ein Test", "DE-de", typeof(TestPlugin))] + [InlineData("unit.test.message", "This is a test", "en", typeof(TestPlugin))] + public void I18N(string key, string excepted, string cultureName = null, Type plugin = null) + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var pluginContext = ComponentManager.PluginManager.GetPlugin(plugin); + + if (cultureName == null) + { + // test execution + var result = InternationalizationManager.I18N(key); + + Assert.Equal(excepted, result); + } + else + { + // preconditions + var culture = CultureInfo.GetCultureInfo(cultureName); + + // test execution + var result = InternationalizationManager.I18N(culture, pluginContext?.PluginId, key); + + Assert.Equal(excepted, result); + } + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index 597941e..3a15cd8 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -121,7 +121,7 @@ public void RemoveEvent() /// Test the get plugin function of the plugin manager. /// [Fact] - public void GetPlugin() + public void GetPluginById() { lock (_lock) { @@ -139,6 +139,28 @@ public void GetPlugin() } } + /// + /// Test the get plugin function of the plugin manager. + /// + [Fact] + public void GetPluginByType() + { + lock (_lock) + { + // preconditions + fixture.RegisterPlugin(typeof(TestPlugin)); + var plugin1 = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + + // test execution + var plugin2 = ComponentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + Assert.Equal(plugin1, plugin2); + + // postconditions + ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + } + } + /// /// Test the event of the plugin manager. /// diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs index be4c5e0..336fa67 100644 --- a/src/WebExpress.WebCore.Test/TestPlugin.cs +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy plugin for testing purposes. /// - [Name("webexpress.webui.unittest")] + [Name("webexpress.webcore.unittest")] [Description("plugin.description")] [Icon("/assets/img/Logo.png")] public sealed class TestPlugin : IPlugin diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index d053309..0b4724d 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -10,6 +10,8 @@ + + @@ -27,6 +29,8 @@ + + From 9347d48625924cb9c321200ebc8c9b10622c9f33 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 24 Sep 2024 22:34:21 +0200 Subject: [PATCH 012/162] restructured and expanded tests - restructured existing tests to improve clarity and maintainability. - added new tests to cover additional use cases. - ensured all tests pass successfully. --- .../Fixture/UnitTestControlFixture.cs | 115 ++++++-- .../Manager/UnitTestApplication.cs | 143 +++++++--- .../Manager/UnitTestInternationalization.cs | 67 +++-- .../Manager/UnitTestPlugin.cs | 264 ++++++++++-------- ...TestApplication.cs => TestApplicationA.cs} | 6 +- .../TestApplicationB.cs | 37 +++ .../TestApplicationC.cs | 38 +++ src/WebExpress.WebCore.Test/TestModule.cs | 2 +- src/WebExpress.WebCore.Test/TestPlugin.cs | 2 +- 9 files changed, 477 insertions(+), 197 deletions(-) rename src/WebExpress.WebCore.Test/{TestApplication.cs => TestApplicationA.cs} (86%) create mode 100644 src/WebExpress.WebCore.Test/TestApplicationB.cs create mode 100644 src/WebExpress.WebCore.Test/TestApplicationC.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index c0cba4f..43e2068 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -4,6 +4,8 @@ using System.Net; using System.Reflection; using System.Text; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; @@ -26,38 +28,112 @@ public class UnitTestControlFixture : IDisposable /// public UnitTestControlFixture() { + } + + /// + /// Create a plugin. + /// + /// The plugin manager. + public static PluginManager CreatePluginManager() + { + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + lock (guard) { - var initializationComponentManager = typeof(ComponentManager).GetMethod("Initialization", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, [typeof(IHttpServerContext)]); - var serverContext = new HttpServerContext - ( - "localhost", - [], - "", - Environment.CurrentDirectory, - Environment.CurrentDirectory, - Environment.CurrentDirectory, - null, - CultureInfo.GetCultureInfo("en"), - new Log() { LogMode = LogMode.Off }, - null - ); - - initializationComponentManager.Invoke(null, [serverContext]); + var ctorPluginManager = typeof(PluginManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); + + var pluginManager = (PluginManager)ctorPluginManager.Invoke([]); + pluginManager.Initialization(serverContext); + + return pluginManager; } } /// /// Register a plugin. /// - /// The plugin to be registered. - public void RegisterPlugin(Type plugin) + /// The plugin manager. + /// The assembly to be registered + public static void RegisterPluginManager(PluginManager pluginManager, Assembly assembly) { lock (guard) { var registerPluginManager = typeof(PluginManager).GetMethod("Register", BindingFlags.NonPublic | BindingFlags.Instance, [typeof(Assembly), typeof(PluginLoadContext)]); + registerPluginManager.Invoke(pluginManager, [assembly, null]); + } + } - registerPluginManager.Invoke(ComponentManager.PluginManager, [plugin.Assembly, null]); + /// + /// Create a internationalization manager. + /// + /// The internationalization manager. + public static InternationalizationManager CreateInternationalizationManager() + { + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + + lock (guard) + { + var ctorPluginManager = typeof(InternationalizationManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); + + var internationalizationManager = (InternationalizationManager)ctorPluginManager.Invoke([]); + internationalizationManager.Initialization(serverContext); + + return internationalizationManager; + } + } + + /// + /// Create a application. + /// + /// The application manager. + public static ApplicationManager CreateApplicationManager() + { + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + + lock (guard) + { + var ctorPluginManager = typeof(ApplicationManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); + + var applicationManager = (ApplicationManager)ctorPluginManager.Invoke([]); + applicationManager.Initialization(serverContext); + + return applicationManager; } } @@ -140,6 +216,7 @@ public WebMessage.Request CrerateRequest(string content = "") new Log() { LogMode = LogMode.Off }, null ); + var headers = (RequestHeaderFields)ctorRequestHeaderFields.Invoke([featureCollection]); var request = (WebMessage.Request)ctorRequest.Invoke([featureCollection, serverContext, headers]); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index d9e1637..f5542b2 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -1,5 +1,4 @@ using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.Test.Manager { @@ -10,35 +9,25 @@ namespace WebExpress.WebCore.Test.Manager [Collection("NonParallelTests")] public class UnitTestApplication(UnitTestControlFixture fixture) : IClassFixture { - private static readonly object _lock = new object(); - /// /// Test the register function of the application manager. /// [Fact] public void Register() { - lock (_lock) - { - // preconditions - var i = 0; - bool triggered = false; - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); - ComponentManager.ApplicationManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - ComponentManager.ApplicationManager.AddApplication += (s, e) => { i++; triggered = true; }; - - // test execution - ComponentManager.ApplicationManager.Register(plugin); - - Assert.Single(ComponentManager.ApplicationManager.Applications); - Assert.Equal("webexpress.webcore.test.testapplication", ComponentManager.ApplicationManager.Applications.FirstOrDefault().ApplicationId); - Assert.Equal(1, i); - Assert.True(triggered); - - // postconditions - ComponentManager.ApplicationManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + applicationManager.Register(plugin); + + Assert.Equal(3, applicationManager.Applications.Count()); + Assert.Equal("webexpress.webcore.test.testapplicationa", applicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationb", applicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); + Assert.Equal("testapplicationc", applicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); } /// @@ -47,17 +36,105 @@ public void Register() [Fact] public void Remove() { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + applicationManager.Register(plugin); + + // test execution + applicationManager.Remove(plugin); + + Assert.Empty(applicationManager.Applications); + } + + /// + /// Test the name property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "webexpress.webcore.test.testapplicationa")] + [InlineData(typeof(TestApplicationB), "webexpress.webcore.test.testapplicationb")] + [InlineData(typeof(TestApplicationC), "testapplicationc")] + public void GetId(Type applicationType, string id) + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + applicationManager.Register(plugin); + + // test execution + var applcation = applicationManager.GetApplcation(applicationType); + + Assert.Equal(id, applcation.ApplicationId); + } + + /// + /// Test the name property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "TestApplicationA")] + [InlineData(typeof(TestApplicationB), "TestApplicationB")] + [InlineData(typeof(TestApplicationC), "TestApplicationC")] + public void GetName(Type applicationType, string name) + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + applicationManager.Register(plugin); + + // test execution + var applcation = applicationManager.GetApplcation(applicationType); + + Assert.Equal(name, applcation.ApplicationName); + } + + /// + /// Test the description property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "application.description")] + [InlineData(typeof(TestApplicationB), "application.description")] + [InlineData(typeof(TestApplicationC), "application.description")] + public void GetDescription(Type applicationType, string description) + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + applicationManager.Register(plugin); + + // test execution + var applcation = applicationManager.GetApplcation(applicationType); + + Assert.Equal(description, applcation.Description); + } + + /// + /// Test the icon property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationB), "/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationC), "/assets/img/Logo.png")] + public void GetIcon(Type applicationType, string icon) + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var applicationManager = UnitTestControlFixture.CreateApplicationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + applicationManager.Register(plugin); - // test execution - ComponentManager.ApplicationManager.Remove(plugin); + // test execution + var applcation = applicationManager.GetApplcation(applicationType); - Assert.Empty(ComponentManager.ApplicationManager.Applications); - } + Assert.Equal(icon, applcation.Icon); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 03a4963..565765f 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -1,7 +1,6 @@ using System.Globalization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.Test.Manager { @@ -12,42 +11,56 @@ namespace WebExpress.WebCore.Test.Manager [Collection("NonParallelTests")] public class UnitTestInternationalization(UnitTestControlFixture fixture) : IClassFixture { - private static readonly object _lock = new object(); + /// + /// Test the register function of the internationalization manager. + /// + [Fact] + public void Register() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + internationalizationManager.Register(plugin); + } /// /// Test the I18N function of the plugin manager. /// [Theory] - [InlineData("webexpress.webcore.test.testplugin:unit.test.message", "Dies ist ein Test", "de")] - [InlineData("webexpress.webcore.test.testplugin:unit.test.message", "This is a test", null, typeof(TestPlugin))] - [InlineData("unit.test.message", "Dies ist ein Test", "de", typeof(TestPlugin))] - [InlineData("unit.test.message", "Dies ist ein Test", "DE-de", typeof(TestPlugin))] - [InlineData("unit.test.message", "This is a test", "en", typeof(TestPlugin))] - public void I18N(string key, string excepted, string cultureName = null, Type plugin = null) + [InlineData("webexpress.webcore.test:unit.test.message", "Dies ist ein Test", "de")] + [InlineData("webexpress.webcore.test:unit.test.message", "This is a test", null)] + [InlineData("unit.test.message", "Dies ist ein Test", "de")] + [InlineData("unit.test.message", "Dies ist ein Test", "DE-de")] + [InlineData("unit.test.message", "This is a test", "en")] + public void I18N(string key, string excepted, string cultureName = null) { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var pluginContext = ComponentManager.PluginManager.GetPlugin(plugin); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + internationalizationManager.Register(plugin); - if (cultureName == null) - { - // test execution - var result = InternationalizationManager.I18N(key); + if (cultureName == null) + { + // test execution + var result = InternationalizationManager.I18N(key); - Assert.Equal(excepted, result); - } - else - { - // preconditions - var culture = CultureInfo.GetCultureInfo(cultureName); + Assert.Equal(excepted, result); + } + else + { + // preconditions + var culture = CultureInfo.GetCultureInfo(cultureName); - // test execution - var result = InternationalizationManager.I18N(culture, pluginContext?.PluginId, key); + // test execution + var result = InternationalizationManager.I18N(culture, plugin?.PluginId, key); - Assert.Equal(excepted, result); - } + Assert.Equal(excepted, result); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index 3a15cd8..4123209 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -1,5 +1,4 @@ using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.Test.Manager { @@ -10,25 +9,20 @@ namespace WebExpress.WebCore.Test.Manager [Collection("NonParallelTests")] public class UnitTestPlugin(UnitTestControlFixture fixture) : IClassFixture { - private static readonly object _lock = new object(); - /// /// Test the register function of the plugin manager. /// [Fact] public void Register() { - lock (_lock) - { - // test execution - ComponentManager.PluginManager.Register(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); - Assert.Single(ComponentManager.PluginManager.Plugins); - Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); + // test execution + pluginManager.Register(); - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + Assert.Single(pluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); } /// @@ -37,17 +31,14 @@ public void Register() [Fact] public void RegisterAssembly() { - lock (_lock) - { - // test execution - fixture.RegisterPlugin(typeof(TestPlugin)); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); - Assert.Single(ComponentManager.PluginManager.Plugins); - Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); + // test execution + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + Assert.Single(pluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); } /// @@ -56,24 +47,20 @@ public void RegisterAssembly() [Fact] public void RegisterEvent() { - lock (_lock) - { - // preconditions - var i = 0; - bool triggered = false; - ComponentManager.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; - - // test execution - fixture.RegisterPlugin(typeof(TestPlugin)); - - Assert.Single(ComponentManager.PluginManager.Plugins); - Assert.Equal("webexpress.webcore.test.testplugin", ComponentManager.PluginManager.Plugins.FirstOrDefault().PluginId); - Assert.Equal(1, i); - Assert.True(triggered); - - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + var i = 0; + var triggered = false; + + pluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; + + // test execution + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + + Assert.Single(pluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + Assert.Equal(1, i); + Assert.True(triggered); } /// @@ -82,17 +69,15 @@ public void RegisterEvent() [Fact] public void Remove() { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var plugin = pluginManager.Plugins.Where(x => x.PluginId == "webexpress.webcore.test").FirstOrDefault(); - // test execution - ComponentManager.PluginManager.Remove(plugin); + // test execution + pluginManager.Remove(plugin); - Assert.Empty(ComponentManager.PluginManager.Plugins); - } + Assert.Empty(pluginManager.Plugins); } /// @@ -101,20 +86,21 @@ public void Remove() [Fact] public void RemoveEvent() { - lock (_lock) - { - // preconditions - var i = 1; - ComponentManager.PluginManager.RemovePlugin += (s, e) => i--; - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); - - // test execution - ComponentManager.PluginManager.Remove(plugin); - - Assert.Empty(ComponentManager.PluginManager.Plugins); - Assert.Equal(0, i); - } + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var plugin = pluginManager.Plugins.Where(x => x.PluginId == "webexpress.webcore.test").FirstOrDefault(); + var i = 1; + var triggered = false; + + pluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; + + // test execution + pluginManager.Remove(plugin); + + Assert.Empty(pluginManager.Plugins); + Assert.Equal(0, i); + Assert.True(triggered); } /// @@ -123,86 +109,138 @@ public void RemoveEvent() [Fact] public void GetPluginById() { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin1 = ComponentManager.PluginManager.Plugins.FirstOrDefault(); - - // test execution - var plugin2 = ComponentManager.PluginManager.GetPlugin(plugin1.PluginId); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - Assert.Equal(plugin1, plugin2); + // test execution + var plugin = pluginManager.GetPlugin("webexpress.webcore.test"); - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } /// - /// Test the get plugin function of the plugin manager. + /// Test the name property of the plugin. /// [Fact] - public void GetPluginByType() + public void GetId() { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin1 = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - // test execution - var plugin2 = ComponentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + // test execution + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal(plugin1, plugin2); + Assert.Equal(typeof(TestPlugin).Namespace.ToLower(), plugin.PluginId); + } - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + /// + /// Test the name property of the plugin. + /// + [Fact] + public void GetName() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + + // test execution + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + Assert.Equal("TestPlugin", plugin.PluginName); } /// - /// Test the event of the plugin manager. + /// Test the description property of the plugin. /// [Fact] - public void Boot() + public void GetDescription() { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - // test execution - ComponentManager.PluginManager.Boot(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + // test execution + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Single(ComponentManager.PluginManager.Plugins); + Assert.Equal("plugin.description", plugin.Description); + } - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + /// + /// Test the icon property of the plugin. + /// + [Fact] + public void GetIcon() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + + // test execution + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + Assert.Equal("/assets/img/Logo.png", plugin.Icon); } /// - /// Test the event of the plugin manager. + /// Test the get plugin function of the plugin manager. /// [Fact] - public void ShutDown() + public void GetPluginByType() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + + // test execution + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + Assert.Equal("webexpress.webcore.test", plugin?.PluginId); + } + + /// + /// Test the boot function of the plugin manager. + /// + [Theory] + [InlineData("webexpress.webcore.test")] + [InlineData("non.existent.plugin")] + [InlineData("")] + [InlineData(null)] + public void Boot(string pluginId) { - lock (_lock) - { - // preconditions - fixture.RegisterPlugin(typeof(TestPlugin)); - var plugin = ComponentManager.PluginManager.Plugins.FirstOrDefault(); + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var plugin = pluginManager.Plugins.Where(x => x.PluginId == pluginId).FirstOrDefault(); + + // test execution + pluginManager.Boot(plugin); - // test execution - ComponentManager.PluginManager.ShutDown(ComponentManager.PluginManager.Plugins.FirstOrDefault()); + Assert.Single(pluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + } + + /// + /// Test the shut down of the plugin manager. + /// + [Theory] + [InlineData("webexpress.webcore.test")] + [InlineData("non.existent.plugin")] + [InlineData("")] + [InlineData(null)] + public void ShutDown(string pluginId) + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var plugin = pluginManager.Plugins.Where(x => x.PluginId == pluginId).FirstOrDefault(); - Assert.Single(ComponentManager.PluginManager.Plugins); + // test execution + pluginManager.ShutDown(plugin); - // postconditions - ComponentManager.PluginManager.Remove(ComponentManager.PluginManager.Plugins.FirstOrDefault()); - } + Assert.Single(pluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); } } } diff --git a/src/WebExpress.WebCore.Test/TestApplication.cs b/src/WebExpress.WebCore.Test/TestApplicationA.cs similarity index 86% rename from src/WebExpress.WebCore.Test/TestApplication.cs rename to src/WebExpress.WebCore.Test/TestApplicationA.cs index f16ca71..7dde9b4 100644 --- a/src/WebExpress.WebCore.Test/TestApplication.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationA.cs @@ -6,11 +6,11 @@ namespace WebExpress.WebCore.Test /// /// A dummy application for testing purposes. /// - [Name("webexpress.webui.unittest")] - [Description("plugin.description")] + [Name("TestApplicationA")] + [Description("application.description")] [Icon("/assets/img/Logo.png")] [Dependency("webexpress.webui")] - public sealed class TestApplication : IApplication + public sealed class TestApplicationA : IApplication { /// /// Initialization of the application. diff --git a/src/WebExpress.WebCore.Test/TestApplicationB.cs b/src/WebExpress.WebCore.Test/TestApplicationB.cs new file mode 100644 index 0000000..5fa38d2 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestApplicationB.cs @@ -0,0 +1,37 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy application for testing purposes. + /// + [Name("TestApplicationB")] + [Description("application.description")] + [Icon("/assets/img/Logo.png")] + [Dependency("webexpress.webui")] + public sealed class TestApplicationB : IApplication + { + /// + /// Initialization of the application. + /// + /// The application context. + public void Initialization(IApplicationContext applicationContext) + { + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestApplicationC.cs b/src/WebExpress.WebCore.Test/TestApplicationC.cs new file mode 100644 index 0000000..06e9677 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestApplicationC.cs @@ -0,0 +1,38 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy application for testing purposes. + /// + [Id("TestApplicationC")] + [Name("TestApplicationC")] + [Description("application.description")] + [Icon("/assets/img/Logo.png")] + [Dependency("webexpress.webui")] + public sealed class TestApplicationC : IApplication + { + /// + /// Initialization of the application. + /// + /// The application context. + public void Initialization(IApplicationContext applicationContext) + { + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModule.cs b/src/WebExpress.WebCore.Test/TestModule.cs index 7980556..8acf991 100644 --- a/src/WebExpress.WebCore.Test/TestModule.cs +++ b/src/WebExpress.WebCore.Test/TestModule.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy module for testing purposes. /// - [Application()] + [Application()] [Name("module.name")] [Description("module.description")] [Icon("/assets/img/Logo.png")] diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs index 336fa67..d39d3b6 100644 --- a/src/WebExpress.WebCore.Test/TestPlugin.cs +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy plugin for testing purposes. /// - [Name("webexpress.webcore.unittest")] + [Name("TestPlugin")] [Description("plugin.description")] [Icon("/assets/img/Logo.png")] public sealed class TestPlugin : IPlugin From 12782c19404f8f1a292cdfc8d649f5a36d364864 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 24 Sep 2024 22:36:17 +0200 Subject: [PATCH 013/162] general improvements considering the concept - implemented general improvements in the code. - adjusted the code to align with the new concept. --- .../Internationalization/de | 1 + .../Internationalization/en | 1 + .../WebApplication/ApplicationItem.cs | 8 ++++- .../WebApplication/ApplicationManager.cs | 33 ++++++++++++++++++- .../WebAttribute/IdAttribute.cs | 17 ++++++++++ .../WebComponent/ComponentManager.cs | 5 +++ src/WebExpress.WebCore/WebJob/JobManager.cs | 5 +++ .../WebModule/ModuleManager.cs | 5 +++ .../WebPlugin/PluginManager.cs | 33 +++++++++++++++---- .../WebResource/ResourceManager.cs | 5 +++ 10 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/IdAttribute.cs diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 526b63b..0d9d472 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -59,6 +59,7 @@ pluginmanager.initialization=Der Pluginmanager wurde initialisiert. pluginmanager.load={0}.dll wird geladen. Version = '{1}' pluginmanager.created=Das Plugin '{0}' wurde erstellt und im PluginManager registriert. pluginmanager.duplicate=Das Plugin '{0}' wurde bereits im PluginManager registriert. +pluginmanager.tomany=Es ist mehr als eine Pluginklasse vorhanden! Das Plugin '{0}' wird nicht geladen. pluginmanager.notavailable=Das Plugin '{0}' ist im PluginManager nicht vorhanden. pluginmanager.plugin=Plugin: '{0}' pluginmanager.pluginmanager.label=Plugin Manager: diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index a46a697..b4b4ebc 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -59,6 +59,7 @@ pluginmanager.initialization=The plugin manager has been initialized. pluginmanager.load={0}.dll is loading. Version = '{1}' pluginmanager.created=The plugin '{0}' was created and registered in the plugin manager. pluginmanager.duplicate=The plugin '{0}' has already been registered in plugin manager. +pluginmanager.tomany=There is more than one plugin class! The plugin '{0}' is not loading. pluginmanager.notavailable=The plugin '{0}' does not exist in plugin manager. pluginmanager.plugin=Plugin: '{0}' pluginmanager.pluginmanager.label=Plugin manager: diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs b/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs index d6cca18..e148cc0 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; namespace WebExpress.WebCore.WebApplication { @@ -12,6 +13,11 @@ internal class ApplicationItem /// public IApplicationContext ApplicationContext { get; set; } + /// + /// Returns the application class. + /// + public Type ApplicationClass { get; internal set; } + /// /// The application. /// diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 4d4f706..e449220 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -110,7 +110,11 @@ public void Register(IPluginContext pluginContext) foreach (var customAttribute in type.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IApplicationAttribute)))) { - if (customAttribute.AttributeType == typeof(NameAttribute)) + if (customAttribute.AttributeType == typeof(IdAttribute)) + { + id = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()?.ToLower() ?? id; + } + else if (customAttribute.AttributeType == typeof(NameAttribute)) { name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } @@ -174,6 +178,7 @@ public void Register(IPluginContext pluginContext) { pluginDict.Add(id, new ApplicationItem() { + ApplicationClass = type, ApplicationContext = applicationContext, Application = applicationInstance }); @@ -230,6 +235,27 @@ public IApplicationContext GetApplcation(string applicationId) return null; } + /// + /// Determines the application contexts for a given application id. + /// + /// The application type. + /// The context of the application or null. + public IApplicationContext GetApplcation(Type application) + { + if (application == null) return null; + + var items = Dictionary.Values.SelectMany(x => x.Values) + .Where(x => x.ApplicationClass.Equals(application)) + .FirstOrDefault(); + + if (items != null) + { + return items.ApplicationContext; + } + + return null; + } + /// /// Determines the application contexts for the given application ids. /// @@ -362,6 +388,11 @@ public void ShutDown(IPluginContext pluginContext) /// The context of the plugin that contains the applications to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + if (!Dictionary.ContainsKey(pluginContext)) { return; diff --git a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs new file mode 100644 index 0000000..ae4ecef --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs @@ -0,0 +1,17 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// The unique identification key. + /// + public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The id. + public IdAttribute(string id) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index bcb3f4b..0104021 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -402,6 +402,11 @@ internal static void ShutDownComponent(IPluginContext pluginContext) /// The context of the plugin that contains the applications to remove. public static void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + if (Dictionary.ContainsKey(pluginContext)) { return; diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 44c62f1..cb4b9d5 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -382,6 +382,11 @@ public void ShutDown() /// The context of the plugin that contains the jobs to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + // the plugin has not been registered in the manager if (!StaticScheduleDictionary.ContainsKey(pluginContext)) { diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index 5db05fe..a378f9d 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -387,6 +387,11 @@ public void ShutDown(IApplicationContext applicationContext) /// The context of the plugin that contains the modules to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + if (!Dictionary.ContainsKey(pluginContext)) { return; diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index eb56d8d..b5dd7f9 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -166,7 +166,7 @@ internal IEnumerable Register(string pluginFile) foreach (var assembly in assemblies) { var pluginContext = Register(assembly, loadContext); - pluginContexts.Add(pluginContext); + pluginContexts.AddRange(pluginContext); } Logging(); @@ -180,8 +180,10 @@ internal IEnumerable Register(string pluginFile) /// The assembly where the plugin is located. /// The plugin load context for isolating and unloading the dependent libraries. /// A plugin created or null. - private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext = null) + private IEnumerable Register(Assembly assembly, PluginLoadContext loadContext = null) { + var plugins = new List(); + try { foreach (var type in assembly @@ -189,7 +191,7 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext .Where(x => x.IsClass && x.IsSealed) .Where(x => x.GetInterface(typeof(IPlugin).Name) != null)) { - var id = $"{type.Namespace?.ToLower()}.{type.Name?.ToLower()}"; + var id = $"{type.Namespace?.ToLower()}"; var name = type.Assembly.GetCustomAttribute()?.Title; var icon = string.Empty; var description = type.Assembly.GetCustomAttribute()?.Description; @@ -199,7 +201,11 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext foreach (var customAttribute in type.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPluginAttribute)))) { - if (customAttribute.AttributeType == typeof(NameAttribute)) + if (customAttribute.AttributeType == typeof(IdAttribute)) + { + id = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()?.ToLower() ?? id; + } + else if (customAttribute.AttributeType == typeof(NameAttribute)) { name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } @@ -271,7 +277,17 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext ); } - return pluginContext; + if (plugins.Any()) + { + plugins.Add(pluginContext); + } + else + { + HttpServerContext.Log.Warning + ( + InternationalizationManager.I18N("webexpress:pluginmanager.tomany", type.FullName) + ); + } } } catch (Exception ex) @@ -279,7 +295,7 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext HttpServerContext.Log.Exception(ex); } - return null; + return plugins; } /// @@ -288,6 +304,11 @@ private IPluginContext Register(Assembly assembly, PluginLoadContext loadContext /// The context of the plugin that contains the elemets to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + OnRemovePlugin(pluginContext); var pluginItem = GetPluginItem(pluginContext); diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index f17e219..89d0e97 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -338,6 +338,11 @@ public IResourceContext GetResorces(string applicationId, string moduleId, strin /// The context of the plugin that contains the resources to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + // the plugin has not been registered in the manager if (!Dictionary.ContainsKey(pluginContext)) { From bc7ce83cfa2f9879c77cc257446b5c39f102ea01 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 24 Sep 2024 22:40:20 +0200 Subject: [PATCH 014/162] restructured and expanded tests - restructured existing tests to improve clarity and maintainability. - added new tests to cover additional use cases. - ensured all tests pass successfully. --- .../Fixture/UnitTestControlFixture.cs | 19 +++++++++++++++++++ .../Manager/UnitTestInternationalization.cs | 1 + 2 files changed, 20 insertions(+) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 43e2068..89ef3b6 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -28,6 +28,25 @@ public class UnitTestControlFixture : IDisposable /// public UnitTestControlFixture() { + lock (guard) + { + var initializationComponentManager = typeof(ComponentManager).GetMethod("Initialization", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, [typeof(IHttpServerContext)]); + var serverContext = new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + + initializationComponentManager.Invoke(null, [serverContext]); + } } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 565765f..85a806a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -36,6 +36,7 @@ public void Register() [InlineData("unit.test.message", "Dies ist ein Test", "de")] [InlineData("unit.test.message", "Dies ist ein Test", "DE-de")] [InlineData("unit.test.message", "This is a test", "en")] + [InlineData("non.existent.key", "non.existent.key", "en")] public void I18N(string key, string excepted, string cultureName = null) { // preconditions From ee7b74d31c4274aa0ff9aa781cbd7a7f7def8dc8 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 25 Sep 2024 21:48:48 +0200 Subject: [PATCH 015/162] increased test coverage for internationalization manager --- .../Internationalization/de | 3 +- .../Internationalization/en | 6 +- .../Manager/UnitTestInternationalization.cs | 105 ++++++++++++++++-- 3 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Internationalization/de b/src/WebExpress.WebCore.Test/Internationalization/de index fb13f3f..5d7a4b0 100644 --- a/src/WebExpress.WebCore.Test/Internationalization/de +++ b/src/WebExpress.WebCore.Test/Internationalization/de @@ -1,4 +1,5 @@ # Deutsch unit.test.message=Dies ist ein Test - +welcome.message=Willkommen '{0}' zu unserer Anwendung! +logout.button=Abmelden diff --git a/src/WebExpress.WebCore.Test/Internationalization/en b/src/WebExpress.WebCore.Test/Internationalization/en index 8636146..902b8d4 100644 --- a/src/WebExpress.WebCore.Test/Internationalization/en +++ b/src/WebExpress.WebCore.Test/Internationalization/en @@ -1,3 +1,5 @@ -# Englisch +# English -unit.test.message=This is a test \ No newline at end of file +unit.test.message=This is a test +welcome.message=Welcome '{0}' to our application! +logout.button=Logout \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 85a806a..68b45dc 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -15,7 +15,7 @@ public class UnitTestInternationalization(UnitTestControlFixture fixture) : ICla /// Test the register function of the internationalization manager. /// [Fact] - public void Register() + public void RegisterSingle() { // preconditions var pluginManager = UnitTestControlFixture.CreatePluginManager(); @@ -25,19 +25,74 @@ public void Register() // test execution internationalizationManager.Register(plugin); + + Assert.Equal("This is a test", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); + } + + /// + /// Test the register function of the internationalization manager. + /// + [Fact] + public void RegisterMultible() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + internationalizationManager.Register([plugin]); + + Assert.Equal("This is a test", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); + } + + /// + /// Test the remove function of the internationalization manager. + /// + [Fact] + public void Remove() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + internationalizationManager.Register(plugin); + + // test execution + internationalizationManager.Remove(plugin); + + Assert.Equal("webexpress.webcore.test:unit.test.message", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); + } + + /// + /// Test the default culture property of the internationalization manager. + /// + [Fact] + public void GetDefaultCulture() + { + // preconditions + var pluginManager = UnitTestControlFixture.CreatePluginManager(); + UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + + // test execution + Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); } /// /// Test the I18N function of the plugin manager. /// [Theory] + [InlineData("webexpress.webcore.test:unit.test.message", "This is a test")] + [InlineData("webexpress.webcore.test:unit.test.message", "This is a test", "en")] [InlineData("webexpress.webcore.test:unit.test.message", "Dies ist ein Test", "de")] - [InlineData("webexpress.webcore.test:unit.test.message", "This is a test", null)] - [InlineData("unit.test.message", "Dies ist ein Test", "de")] - [InlineData("unit.test.message", "Dies ist ein Test", "DE-de")] - [InlineData("unit.test.message", "This is a test", "en")] - [InlineData("non.existent.key", "non.existent.key", "en")] - public void I18N(string key, string excepted, string cultureName = null) + [InlineData("webexpress.webcore.test:unit.test.message", "Dies ist ein Test", "de", "webexpress.webcore.test")] + [InlineData("webexpress.webcore.test:welcome.message", "Welcome 'Max' to our application!", "en", null, "Max")] + [InlineData("welcome.message", "Welcome 'Max' to our application!", "en", "webexpress.webcore.test", "Max")] + [InlineData("non.existent.key", "non.existent.key", "de")] + public void I18N(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions var pluginManager = UnitTestControlFixture.CreatePluginManager(); @@ -46,23 +101,49 @@ public void I18N(string key, string excepted, string cultureName = null) var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); internationalizationManager.Register(plugin); - if (cultureName == null) + if (cultureName == null && !param.Any()) { // test execution var result = InternationalizationManager.I18N(key); Assert.Equal(excepted, result); } - else + if (cultureName == null && param.Any()) { - // preconditions - var culture = CultureInfo.GetCultureInfo(cultureName); + // test execution + var result = InternationalizationManager.I18N(key, param); + Assert.Equal(excepted, result); + } + if (cultureName != null && pluginID == null && !param.Any()) + { // test execution - var result = InternationalizationManager.I18N(culture, plugin?.PluginId, key); + var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), key); Assert.Equal(excepted, result); } + if (cultureName != null && pluginID == null && param.Any()) + { + // test execution + var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), key, param); + + Assert.Equal(excepted, result); + } + if (cultureName != null && pluginID != null && !param.Any()) + { + // test execution + var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), pluginID, key); + + Assert.Equal(excepted, result); + } + if (cultureName != null && pluginID != null && param.Any()) + { + // test execution + var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), pluginID, key, param); + + Assert.Equal(excepted, result); + } + } } } From b4cba8d5ec792070e5fd3d6321f77a8a52984e85 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 25 Sep 2024 21:50:26 +0200 Subject: [PATCH 016/162] added new features to internationalizationmanager --- .../InternationalizationManager.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 66203cd..cd81c59 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -134,7 +134,20 @@ internal static void Register(Assembly assembly, string pluginId) /// The context of the plugin containing the key-value pairs to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + + foreach (var dictionary in Dictionary.Values) + { + var keysToRemove = dictionary.Keys.Where(k => k.StartsWith($"{pluginContext?.PluginId}:")).ToList(); + foreach (var key in keysToRemove) + { + dictionary.Remove(key); + } + } } /// @@ -148,6 +161,18 @@ public static string I18N(II18N obj, string key) return I18N(obj.Culture, key); } + /// + /// Internationalization of a key. + /// + /// An internationalization object that is being extended. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string I18N(II18N obj, string key, params object[] args) + { + return string.Format(I18N(obj, key), args); + } + /// /// Internationalization of a key. /// @@ -159,6 +184,18 @@ public static string I18N(Request request, string key) return I18N(request.Culture, null, key); } + /// + /// Internationalization of a key. + /// + /// The request with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string I18N(Request request, string key, params object[] args) + { + return string.Format(I18N(request, key), args); + } + /// /// Internationalization of a key. /// @@ -170,6 +207,18 @@ public static string I18N(CultureInfo culture, string key) return I18N(culture, null, key); } + /// + /// Internationalization of a key. + /// + /// The culture with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string I18N(CultureInfo culture, string key, params object[] args) + { + return string.Format(I18N(culture, key), args); + } + /// /// Internationalization of a key. /// @@ -208,6 +257,19 @@ public static string I18N(CultureInfo culture, string pluginId, string key) return key; } + /// + /// Internationalization of a key. + /// + /// The culture with the language to use. + /// The plugin id. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string I18N(CultureInfo culture, string pluginId, string key, params object[] args) + { + return string.Format(I18N(culture, pluginId, key), args); + } + /// /// Internationalization of a key. /// From f78f2b6883e8dc84b2e9a453a2b27897789c62b3 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 28 Sep 2024 00:08:26 +0200 Subject: [PATCH 017/162] refactor code and tests - improved code readability and maintainability - updated and optimized test cases - removed redundant code and tests --- .../Fixture/UnitTestControlFixture.cs | 154 +++--------------- .../Html/UnitTestHtmlElementFieldLabel.cs | 1 + .../Html/UnitTestHtmlElementTextContentP.cs | 1 + .../Html/UnitTestHtmlText.cs | 1 + .../Manager/UnitTestApplication.cs | 70 +++----- .../Manager/UnitTestInternationalization.cs | 52 ++---- .../Manager/UnitTestModule.cs | 119 ++++++++++++++ .../Manager/UnitTestPlugin.cs | 143 +++++++--------- .../Message/UnitTestGetRequest.cs | 24 +-- .../Request/UnitTestRequest.cs | 6 +- .../Schedule/UnitTestClock.cs | 2 +- .../Schedule/UnitTestCron.cs | 112 +++++-------- .../{TestModule.cs => TestModuleA1.cs} | 6 +- src/WebExpress.WebCore.Test/TestModuleA2.cs | 40 +++++ src/WebExpress.WebCore.Test/TestPage.cs | 2 +- src/WebExpress.WebCore.Test/TestPlugin.cs | 5 + .../Uri/UnitTestUriAbsolute.cs | 5 +- .../Uri/UnitTestUriRelative.cs | 5 +- .../Uri/UnitTestUriRelativeAppend.cs | 5 +- .../Uri/UnitTestUriRelativeExtendedPath.cs | 5 +- .../Uri/UnitTestUriRelativeSkip.cs | 5 +- .../Uri/UnitTestUriRelativeTake.cs | 5 +- src/WebExpress.WebCore/HttpServer.cs | 14 +- .../InternationalizationManager.cs | 10 +- .../WebApplication/ApplicationManager.cs | 10 +- .../WebComponent/ComponentManager.cs | 112 +++++++------ .../WebEvent/EventManager.cs | 10 +- src/WebExpress.WebCore/WebEx.cs | 150 ++++++++++++++++- src/WebExpress.WebCore/WebJob/JobManager.cs | 10 +- .../WebMessage/HttpContext.cs | 6 +- .../WebMessage/ParameterDictionary.cs | 4 +- src/WebExpress.WebCore/WebMessage/Request.cs | 27 +-- .../WebModule/ModuleManager.cs | 32 +++- .../WebPackage/PackageManager.cs | 10 +- .../WebPlugin/PluginManager.cs | 12 +- .../WebResource/ResourceContext.cs | 3 +- .../WebResource/ResourceManager.cs | 10 +- .../WebSitemap/SitemapManager.cs | 10 +- .../WebStatusPage/StatusPageManager.cs | 10 +- src/WebExpress.WebCore/WebTask/Task.cs | 5 +- 40 files changed, 713 insertions(+), 500 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs rename src/WebExpress.WebCore.Test/{TestModule.cs => TestModuleA1.cs} (88%) create mode 100644 src/WebExpress.WebCore.Test/TestModuleA2.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 89ef3b6..062dc92 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -4,14 +4,11 @@ using System.Net; using System.Reflection; using System.Text; -using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; namespace WebExpress.WebCore.Test.Fixture @@ -28,34 +25,15 @@ public class UnitTestControlFixture : IDisposable /// public UnitTestControlFixture() { - lock (guard) - { - var initializationComponentManager = typeof(ComponentManager).GetMethod("Initialization", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static, [typeof(IHttpServerContext)]); - var serverContext = new HttpServerContext - ( - "localhost", - [], - "", - Environment.CurrentDirectory, - Environment.CurrentDirectory, - Environment.CurrentDirectory, - null, - CultureInfo.GetCultureInfo("en"), - new Log() { LogMode = LogMode.Off }, - null - ); - - initializationComponentManager.Invoke(null, [serverContext]); - } } /// - /// Create a plugin. + /// Create a a server context. /// - /// The plugin manager. - public static PluginManager CreatePluginManager() + /// The server context. + public static HttpServerContext CreateHttpServerContext() { - var serverContext = new HttpServerContext + return new HttpServerContext ( "localhost", [], @@ -68,92 +46,25 @@ public static PluginManager CreatePluginManager() new Log() { LogMode = LogMode.Off }, null ); - - lock (guard) - { - var ctorPluginManager = typeof(PluginManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); - - var pluginManager = (PluginManager)ctorPluginManager.Invoke([]); - pluginManager.Initialization(serverContext); - - return pluginManager; - } } /// - /// Register a plugin. + /// Create a component manager. /// - /// The plugin manager. - /// The assembly to be registered - public static void RegisterPluginManager(PluginManager pluginManager, Assembly assembly) + /// The component manager. + public static ComponentManager CreateComponentManager() { - lock (guard) - { - var registerPluginManager = typeof(PluginManager).GetMethod("Register", BindingFlags.NonPublic | BindingFlags.Instance, [typeof(Assembly), typeof(PluginLoadContext)]); - registerPluginManager.Invoke(pluginManager, [assembly, null]); - } - } - - /// - /// Create a internationalization manager. - /// - /// The internationalization manager. - public static InternationalizationManager CreateInternationalizationManager() - { - var serverContext = new HttpServerContext + var ctorComponentManager = typeof(ComponentManager).GetConstructor ( - "localhost", - [], - "", - Environment.CurrentDirectory, - Environment.CurrentDirectory, - Environment.CurrentDirectory, + BindingFlags.NonPublic | BindingFlags.Instance, null, - CultureInfo.GetCultureInfo("en"), - new Log() { LogMode = LogMode.Off }, + [typeof(HttpServerContext)], null ); - lock (guard) - { - var ctorPluginManager = typeof(InternationalizationManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); - - var internationalizationManager = (InternationalizationManager)ctorPluginManager.Invoke([]); - internationalizationManager.Initialization(serverContext); + var componentManager = (ComponentManager)ctorComponentManager.Invoke([CreateHttpServerContext()]); - return internationalizationManager; - } - } - - /// - /// Create a application. - /// - /// The application manager. - public static ApplicationManager CreateApplicationManager() - { - var serverContext = new HttpServerContext - ( - "localhost", - [], - "", - Environment.CurrentDirectory, - Environment.CurrentDirectory, - Environment.CurrentDirectory, - null, - CultureInfo.GetCultureInfo("en"), - new Log() { LogMode = LogMode.Off }, - null - ); - - lock (guard) - { - var ctorPluginManager = typeof(ApplicationManager).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); - - var applicationManager = (ApplicationManager)ctorPluginManager.Invoke([]); - applicationManager.Initialization(serverContext); - - return applicationManager; - } + return componentManager; } /// @@ -161,10 +72,10 @@ public static ApplicationManager CreateApplicationManager() /// /// The content. /// A fake request for testing. - public WebMessage.Request CrerateRequest(string content = "") + public static WebMessage.Request CrerateRequest(string content = "") { var ctorRequestHeaderFields = typeof(RequestHeaderFields).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection)], null); - var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(IHttpServerContext), typeof(RequestHeaderFields)], null); + var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(ComponentManager)], null); var featureCollection = new FeatureCollection(); var firstLine = content.Split('\n').FirstOrDefault(); var lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); @@ -222,22 +133,10 @@ public WebMessage.Request CrerateRequest(string content = "") featureCollection.Set(requestIdentifierFeature); featureCollection.Set(connectionFeature); - var serverContext = new HttpServerContext - ( - "localhost", - [], - "", - "", - "", - "", - null, - null, - new Log() { LogMode = LogMode.Off }, - null - ); + var componentManager = CreateComponentManager(); var headers = (RequestHeaderFields)ctorRequestHeaderFields.Invoke([featureCollection]); - var request = (WebMessage.Request)ctorRequest.Invoke([featureCollection, serverContext, headers]); + var request = (WebMessage.Request)ctorRequest.Invoke([featureCollection, headers, componentManager]); return request; } @@ -246,7 +145,7 @@ public WebMessage.Request CrerateRequest(string content = "") /// Create a fake render context. /// /// A fake context for testing. - public RenderContext CrerateContext() + public static RenderContext CrerateContext() { var request = CrerateRequest(); var page = new TestPage(); @@ -261,12 +160,12 @@ public RenderContext CrerateContext() /// Create a fake resource context. /// /// A fake context for testing. - public ResourceContext CrerateResourceContext() + public static ResourceContext CrerateResourceContext() { var ctorResourceContext = typeof(ResourceContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); - var moduleContext = ComponentManager.ModuleManager.Modules - .Where(x => x.ModuleId == typeof(TestModule).FullName.ToLower()) + var moduleContext = WebEx.ComponentManager.ModuleManager.Modules + .Where(x => x.ModuleId == typeof(TestModuleA1).FullName.ToLower()) .FirstOrDefault(); var resourceContext = (ResourceContext)ctorResourceContext.Invoke([moduleContext]); @@ -279,17 +178,16 @@ public ResourceContext CrerateResourceContext() /// /// The name of the resource file. /// The content of the embedded resource as a string. - public string GetEmbeddedResource(string fileName) + public static string GetEmbeddedResource(string fileName) { - var assembly = GetType().Assembly; + var assembly = typeof(UnitTestControlFixture).Assembly; var resourceName = assembly.GetManifestResourceNames() .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - using (StreamReader reader = new StreamReader(stream)) - { - return reader.ReadToEnd(); - } + using Stream stream = assembly.GetManifestResourceStream(resourceName); + using StreamReader reader = new(stream); + + return reader.ReadToEnd(); } /// diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs index f796872..400b89d 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs @@ -2,6 +2,7 @@ namespace WebExpress.WebCore.Test.Html { + [Collection("NonParallelTests")] public class UnitTestHtmlElementFieldLabel { /// diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs index ad0ee7e..9303a7a 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs @@ -2,6 +2,7 @@ namespace WebExpress.WebCore.Test.Html { + [Collection("NonParallelTests")] public class UnitTestHtmlElementTextContentP { /// diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs index e5bf608..4096eea 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs @@ -2,6 +2,7 @@ namespace WebExpress.WebCore.Test.Html { + [Collection("NonParallelTests")] public class UnitTestHtmlText { /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index f5542b2..0ad52f1 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -5,9 +5,8 @@ namespace WebExpress.WebCore.Test.Manager /// /// Test the application manager. /// - /// The fixture. [Collection("NonParallelTests")] - public class UnitTestApplication(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestApplication { /// /// Test the register function of the application manager. @@ -16,18 +15,15 @@ public class UnitTestApplication(UnitTestControlFixture fixture) : IClassFixture public void Register() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + var componentManager = UnitTestControlFixture.CreateComponentManager(); // test execution - applicationManager.Register(plugin); + componentManager.PluginManager.Register(); - Assert.Equal(3, applicationManager.Applications.Count()); - Assert.Equal("webexpress.webcore.test.testapplicationa", applicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationb", applicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); - Assert.Equal("testapplicationc", applicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); + Assert.Equal(3, componentManager.ApplicationManager.Applications.Count()); + Assert.Equal("webexpress.webcore.test.testapplicationa", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationb", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); + Assert.Equal("testapplicationc", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); } /// @@ -37,16 +33,14 @@ public void Register() public void Remove() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - applicationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - applicationManager.Remove(plugin); + componentManager.ApplicationManager.Remove(plugin); - Assert.Empty(applicationManager.Applications); + Assert.Empty(componentManager.ApplicationManager.Applications); } /// @@ -59,15 +53,11 @@ public void Remove() public void GetId(Type applicationType, string id) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - applicationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution - var applcation = applicationManager.GetApplcation(applicationType); - Assert.Equal(id, applcation.ApplicationId); } @@ -81,15 +71,11 @@ public void GetId(Type applicationType, string id) public void GetName(Type applicationType, string name) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - applicationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution - var applcation = applicationManager.GetApplcation(applicationType); - Assert.Equal(name, applcation.ApplicationName); } @@ -103,15 +89,11 @@ public void GetName(Type applicationType, string name) public void GetDescription(Type applicationType, string description) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - applicationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution - var applcation = applicationManager.GetApplcation(applicationType); - Assert.Equal(description, applcation.Description); } @@ -125,15 +107,11 @@ public void GetDescription(Type applicationType, string description) public void GetIcon(Type applicationType, string icon) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var applicationManager = UnitTestControlFixture.CreateApplicationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - applicationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution - var applcation = applicationManager.GetApplcation(applicationType); - Assert.Equal(icon, applcation.Icon); } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 68b45dc..8d5aa2c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -7,42 +7,20 @@ namespace WebExpress.WebCore.Test.Manager /// /// Test the internationalization manager. /// - /// The fixture. [Collection("NonParallelTests")] - public class UnitTestInternationalization(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestInternationalization { /// /// Test the register function of the internationalization manager. /// [Fact] - public void RegisterSingle() + public void Register() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + var componentManager = UnitTestControlFixture.CreateComponentManager(); // test execution - internationalizationManager.Register(plugin); - - Assert.Equal("This is a test", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); - } - - /// - /// Test the register function of the internationalization manager. - /// - [Fact] - public void RegisterMultible() - { - // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - - // test execution - internationalizationManager.Register([plugin]); + componentManager.PluginManager.Register(); Assert.Equal("This is a test", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); } @@ -54,14 +32,12 @@ public void RegisterMultible() public void Remove() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - internationalizationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - internationalizationManager.Remove(plugin); + componentManager.InternationalizationManager.Remove(plugin); Assert.Equal("webexpress.webcore.test:unit.test.message", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); } @@ -73,9 +49,8 @@ public void Remove() public void GetDefaultCulture() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); // test execution Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); @@ -95,11 +70,8 @@ public void GetDefaultCulture() public void I18N(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var internationalizationManager = UnitTestControlFixture.CreateInternationalizationManager(); - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - internationalizationManager.Register(plugin); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); if (cultureName == null && !param.Any()) { diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs new file mode 100644 index 0000000..9bca744 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -0,0 +1,119 @@ +using WebExpress.WebCore.Test.Fixture; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the module manager. + /// + [Collection("NonParallelTests")] + public class UnitTestModule + { + /// + /// Test the register function of the module manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + + // test execution + componentManager.PluginManager.Register(); + + Assert.Equal(2, componentManager.ModuleManager.Modules.Count()); + } + + /// + /// Test the remove function of the module manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + componentManager.ModuleManager.Remove(plugin); + + Assert.Empty(componentManager.ModuleManager.Modules); + } + + /// + /// Test the id property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "webexpress.webcore.test.testmodulea1")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "webexpress.webcore.test.testmodulea2")] + public void GetId(Type applicationType, Type moduleType, string id) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + Assert.Equal(id, module.ModuleId); + } + + /// + /// Test the name property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.namea1")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.namea2")] + public void GetName(Type applicationType, Type moduleType, string name) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + Assert.Equal(name, module.ModuleName); + } + + /// + /// Test the description property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.descriptiona1")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.descriptiona2")] + public void GetDescription(Type applicationType, Type moduleType, string description) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + Assert.Equal(description, module.Description); + } + + /// + /// Test the icon property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/assets/img/Logo.png")] + public void GetIcon(Type applicationType, Type moduleType, string icon) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + Assert.Equal(icon, module.Icon); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index 4123209..f30561e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -5,9 +5,8 @@ namespace WebExpress.WebCore.Test.Manager /// /// Test the plugin manager. /// - /// The fixture. [Collection("NonParallelTests")] - public class UnitTestPlugin(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestPlugin { /// /// Test the register function of the plugin manager. @@ -16,29 +15,13 @@ public class UnitTestPlugin(UnitTestControlFixture fixture) : IClassFixture x.PluginId)); - } - - /// - /// Test the register function of the plugin manager. - /// - [Fact] - public void RegisterAssembly() - { - // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - - // test execution - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - - Assert.Single(pluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + Assert.Single(componentManager.PluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); } /// @@ -48,17 +31,17 @@ public void RegisterAssembly() public void RegisterEvent() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); var i = 0; var triggered = false; - pluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; + componentManager.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; // test execution - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + componentManager.PluginManager.Register(); - Assert.Single(pluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + Assert.Single(componentManager.PluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); Assert.Equal(1, i); Assert.True(triggered); } @@ -70,14 +53,14 @@ public void RegisterEvent() public void Remove() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var plugin = pluginManager.Plugins.Where(x => x.PluginId == "webexpress.webcore.test").FirstOrDefault(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - pluginManager.Remove(plugin); + componentManager.PluginManager.Remove(plugin); - Assert.Empty(pluginManager.Plugins); + Assert.Empty(componentManager.PluginManager.Plugins); } /// @@ -87,18 +70,18 @@ public void Remove() public void RemoveEvent() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var plugin = pluginManager.Plugins.Where(x => x.PluginId == "webexpress.webcore.test").FirstOrDefault(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); var i = 1; var triggered = false; - pluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; + componentManager.PluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - pluginManager.Remove(plugin); + componentManager.PluginManager.Remove(plugin); - Assert.Empty(pluginManager.Plugins); + Assert.Empty(componentManager.PluginManager.Plugins); Assert.Equal(0, i); Assert.True(triggered); } @@ -110,11 +93,11 @@ public void RemoveEvent() public void GetPluginById() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); // test execution - var plugin = pluginManager.GetPlugin("webexpress.webcore.test"); + var plugin = componentManager.PluginManager.GetPlugin("webexpress.webcore.test"); Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } @@ -126,12 +109,11 @@ public void GetPluginById() public void GetId() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal(typeof(TestPlugin).Namespace.ToLower(), plugin.PluginId); } @@ -142,12 +124,11 @@ public void GetId() public void GetName() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal("TestPlugin", plugin.PluginName); } @@ -158,12 +139,11 @@ public void GetName() public void GetDescription() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal("plugin.description", plugin.Description); } @@ -174,12 +154,11 @@ public void GetDescription() public void GetIcon() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal("/assets/img/Logo.png", plugin.Icon); } @@ -190,11 +169,11 @@ public void GetIcon() public void GetPluginByType() { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); // test execution - var plugin = pluginManager.GetPlugin(typeof(TestPlugin)); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } @@ -203,44 +182,44 @@ public void GetPluginByType() /// Test the boot function of the plugin manager. /// [Theory] - [InlineData("webexpress.webcore.test")] - [InlineData("non.existent.plugin")] - [InlineData("")] - [InlineData(null)] - public void Boot(string pluginId) + [InlineData("webexpress.webcore.test", "webexpress.webcore.test")] + [InlineData("non.existent.plugin", null)] + [InlineData("", null)] + [InlineData(null, null)] + public void Boot(string pluginId, string expected) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var plugin = pluginManager.Plugins.Where(x => x.PluginId == pluginId).FirstOrDefault(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(pluginId); // test execution - pluginManager.Boot(plugin); + componentManager.PluginManager.Boot(plugin); - Assert.Single(pluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + Assert.Single(componentManager.PluginManager.Plugins); + Assert.Equal(expected, plugin?.PluginId); } /// /// Test the shut down of the plugin manager. /// [Theory] - [InlineData("webexpress.webcore.test")] - [InlineData("non.existent.plugin")] - [InlineData("")] - [InlineData(null)] - public void ShutDown(string pluginId) + [InlineData("webexpress.webcore.test", "webexpress.webcore.test")] + [InlineData("non.existent.plugin", null)] + [InlineData("", null)] + [InlineData(null, null)] + public void ShutDown(string pluginId, string expected) { // preconditions - var pluginManager = UnitTestControlFixture.CreatePluginManager(); - UnitTestControlFixture.RegisterPluginManager(pluginManager, typeof(TestPlugin).Assembly); - var plugin = pluginManager.Plugins.Where(x => x.PluginId == pluginId).FirstOrDefault(); + var componentManager = UnitTestControlFixture.CreateComponentManager(); + componentManager.PluginManager.Register(); + var plugin = componentManager.PluginManager.GetPlugin(pluginId); // test execution - pluginManager.ShutDown(plugin); + componentManager.PluginManager.ShutDown(plugin); - Assert.Single(pluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", pluginManager.Plugins.Select(x => x.PluginId)); + Assert.Single(componentManager.PluginManager.Plugins); + Assert.Equal(expected, plugin?.PluginId); } } } diff --git a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs index 2440f2e..34ec888 100644 --- a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs +++ b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs @@ -5,8 +5,8 @@ namespace WebExpress.WebCore.Test.Message /// /// UnitTestGetRequest class for testing HTTP GET requests. /// - /// The fixture used for setting up the tests. - public class UnitTestGetRequest(UnitTestControlFixture fixture) : IClassFixture + [Collection("NonParallelTests")] + public class UnitTestGetRequest { /// /// Tests a general GET request. @@ -14,8 +14,8 @@ public class UnitTestGetRequest(UnitTestControlFixture fixture) : IClassFixture< [Fact] public void General() { - var content = fixture.GetEmbeddedResource("general.get"); - var request = fixture.CrerateRequest(content); + var content = UnitTestControlFixture.GetEmbeddedResource("general.get"); + var request = UnitTestControlFixture.CrerateRequest(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -26,8 +26,8 @@ public void General() [Fact] public void Less() { - var content = fixture.GetEmbeddedResource("less.get"); - var request = fixture.CrerateRequest(content); + var content = UnitTestControlFixture.GetEmbeddedResource("less.get"); + var request = UnitTestControlFixture.CrerateRequest(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -38,8 +38,8 @@ public void Less() [Fact] public void Massive() { - var content = fixture.GetEmbeddedResource("massive.get"); - var request = fixture.CrerateRequest(content); + var content = UnitTestControlFixture.GetEmbeddedResource("massive.get"); + var request = UnitTestControlFixture.CrerateRequest(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -50,8 +50,8 @@ public void Massive() [Fact] public void GetParameter() { - var content = fixture.GetEmbeddedResource("param.get"); - var request = fixture.CrerateRequest(content); + var content = UnitTestControlFixture.GetEmbeddedResource("param.get"); + var request = UnitTestControlFixture.CrerateRequest(content); var param = request?.GetParameter("a")?.Value; Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); @@ -64,8 +64,8 @@ public void GetParameter() [Fact] public void GetParameterWithUmlaut() { - var content = fixture.GetEmbeddedResource("param_umlaut.get"); - var request = fixture.CrerateRequest(content); + var content = UnitTestControlFixture.GetEmbeddedResource("param_umlaut.get"); + var request = UnitTestControlFixture.CrerateRequest(content); var a = request?.GetParameter("a")?.Value; var b = request?.GetParameter("b")?.Value; diff --git a/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs b/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs index cff81c1..064c496 100644 --- a/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs +++ b/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs @@ -1,9 +1,7 @@ -using WebExpress.WebCore.Test.Fixture; - -namespace WebExpress.WebCore.Test.Request +namespace WebExpress.WebCore.Test.Request { [Collection("NonParallelTests")] - public class UnitTestRequest(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestRequest { [Fact] diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs index b06f198..7b86ad2 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test.Schedule /// Tests the scheduler's clock. /// [Collection("NonParallelTests")] - public class UnitTestClock(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestClock { [Fact] public void Synchronize_1() diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs index aa7a247..f25cbda 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs @@ -1,6 +1,4 @@ -using System.Globalization; -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; @@ -10,160 +8,126 @@ namespace WebExpress.WebCore.Test.Schedule /// Test the cron job of the scheduler. /// [Collection("NonParallelTests")] - public class UnitTestCron(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestCron { [Fact] public void Create_1() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); - - ComponentManager.Initialization(context); - + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var clock = new Clock(); var cron = new Cron(context, "0-59", "*", "1-31", "1-2,3,4,5,6,7,8-10,11,12"); - Assert.True - ( - cron.Matching(clock) - ); + // test execution + Assert.True(cron.Matching(clock)); } [Fact] public void Create_2() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 1, dateTime.Day, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "0-33", "2, 1-4, x"); - Assert.True - ( - cron.Matching(clock) - ); + // test execution + Assert.True(cron.Matching(clock)); } [Fact] public void Create_3() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "12"); - Assert.True - ( - cron.Matching(clock) - ); + // test execution + Assert.True(cron.Matching(clock)); } [Fact] public void Create_4() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; Log.Current.Clear(); - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "*", "a"); - Assert.True - ( - context.Log.WarningCount == 1 - ); + // test execution + Assert.Equal(1, context.Log.WarningCount); } [Fact] public void Create_5() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "*", ""); - Assert.True - ( - cron.Matching(clock) - ); + // test execution + Assert.True(cron.Matching(clock)); } [Fact] public void Create_6() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; Log.Current.Clear(); - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "99", "*", "*", "*"); - Assert.True - ( - context.Log.WarningCount == 1 - ); + // test execution + Assert.Equal(1, context.Log.WarningCount); } [Fact] public void Matching_1() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "1-11"); - Assert.True - ( - !cron.Matching(clock) - ); + // test execution + Assert.False(cron.Matching(clock)); } [Fact] public void Matching_2() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "3"); // wednesday - Assert.True - ( - cron.Matching(clock) - ); + // test execution + Assert.True(cron.Matching(clock)); } [Fact] public void Matching_3() { - var context = new HttpServerContext(null, null, null, null, null, null, null, CultureInfo.CurrentCulture, Log.Current, null); + // preconditions + var context = UnitTestControlFixture.CreateHttpServerContext(); var dateTime = DateTime.Now; - - ComponentManager.Initialization(context); - var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "1"); // sunday - Assert.True - ( - !cron.Matching(clock) - ); + // test execution + Assert.False(cron.Matching(clock)); } } } diff --git a/src/WebExpress.WebCore.Test/TestModule.cs b/src/WebExpress.WebCore.Test/TestModuleA1.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestModule.cs rename to src/WebExpress.WebCore.Test/TestModuleA1.cs index 8acf991..74265a5 100644 --- a/src/WebExpress.WebCore.Test/TestModule.cs +++ b/src/WebExpress.WebCore.Test/TestModuleA1.cs @@ -7,12 +7,12 @@ namespace WebExpress.WebCore.Test /// A dummy module for testing purposes. /// [Application()] - [Name("module.name")] - [Description("module.description")] + [Name("module.namea1")] + [Description("module.descriptiona1")] [Icon("/assets/img/Logo.png")] [AssetPath("/")] [ContextPath("/")] - public sealed class TestModule : IModule + public sealed class TestModuleA1 : IModule { /// /// Initialization of the module. diff --git a/src/WebExpress.WebCore.Test/TestModuleA2.cs b/src/WebExpress.WebCore.Test/TestModuleA2.cs new file mode 100644 index 0000000..5aed014 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModuleA2.cs @@ -0,0 +1,40 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + [Name("module.namea2")] + [Description("module.descriptiona2")] + [Icon("/assets/img/Logo.png")] + [AssetPath("/")] + [ContextPath("/")] + public sealed class TestModuleA2 : IModule + { + /// + /// Initialization of the module. + /// + /// The module context. + public void Initialization(IModuleContext moduleContext) + { + throw new NotImplementedException(); + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPage.cs b/src/WebExpress.WebCore.Test/TestPage.cs index 8f76b6f..e99866a 100644 --- a/src/WebExpress.WebCore.Test/TestPage.cs +++ b/src/WebExpress.WebCore.Test/TestPage.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:homepage.label")] [Segment(null, "webindex:homepage.label")] [ContextPath(null)] - [Module] + [Module] public sealed class TestPage : IPage { /// diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs index d39d3b6..01c939c 100644 --- a/src/WebExpress.WebCore.Test/TestPlugin.cs +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -11,6 +11,11 @@ namespace WebExpress.WebCore.Test [Icon("/assets/img/Logo.png")] public sealed class TestPlugin : IPlugin { + public TestPlugin() + { + + } + /// /// Initialization of the plugin. /// diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs index 0e51997..0644eb1 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests an absolute Uri. /// [Collection("NonParallelTests")] - public class UnitTestUriAbsolute(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriAbsolute { [Fact] public void Test_0() diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs index 23f2a36..ced47b7 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests an relative uri. /// [Collection("NonParallelTests")] - public class UnitTestUriRelative(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriRelative { [Fact] public void Test_0() diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs index 5cda0bb..e81bef7 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests the append method. /// [Collection("NonParallelTests")] - public class UnitTestUriRelativeAppend(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriRelativeAppend { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs index 28e5bf8..9516b46 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests the extended path property. /// [Collection("NonParallelTests")] - public class UnitTestUriRelativeExtendedPath(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriRelativeExtendedPath { private readonly UriResource Uri = new UriResource("http://user@example.com:80"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs index e88b631..ed040c8 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests the skip method. /// [Collection("NonParallelTests")] - public class UnitTestUriRelativeSkip(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriRelativeSkip { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs index cf98102..258f918 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -7,7 +6,7 @@ namespace WebExpress.WebCore.Test.Uri /// Tests the take method. /// [Collection("NonParallelTests")] - public class UnitTestUriRelativeTake(UnitTestControlFixture fixture) : IClassFixture + public class UnitTestUriRelativeTake { private readonly UriResource Uri = new UriResource("/a/b/c"); diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 6a3719a..7da2912 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -73,6 +73,11 @@ public class HttpServer : IHost, II18N, IHttpApplication /// public long RequestNumber { get; private set; } + /// + /// Returns the component manager. + /// + public ComponentManager ComponentManager { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -95,7 +100,7 @@ public HttpServer(HttpServerContext context) Culture = HttpServerContext.Culture; - ComponentManager.Initialization(HttpServerContext); + ComponentManager = new ComponentManager(HttpServerContext); } /// @@ -233,9 +238,6 @@ public void Stop() { // End running threads Kestrel.StopAsync(ServerToken); - - // Stop running - ComponentManager.ShutDown(); } /// @@ -262,7 +264,7 @@ private Response HandleClient(HttpContext context) )); // search page in sitemap - var searchResult = ComponentManager.SitemapManager.SearchResource(context.Uri, new SearchContext() + var searchResult = WebEx.ComponentManager.SitemapManager.SearchResource(context.Uri, new SearchContext() { Culture = culture, HttpContext = context, @@ -538,7 +540,7 @@ public HttpContext CreateContext(IFeatureCollection contextFeatures) { try { - return new HttpContext(contextFeatures, this.HttpServerContext); + return new HttpContext(contextFeatures, HttpServerContext, ComponentManager); } catch (Exception ex) { diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index cd81c59..750aefb 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -29,11 +29,19 @@ public sealed class InternationalizationManager : IComponentPlugin, ISystemCompo /// public IHttpServerContext HttpServerContext { get; private set; } + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Initializes a new instance of the class. /// - internal InternationalizationManager() + /// The component manager. + internal InternationalizationManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index e449220..e2c5a55 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -37,6 +37,11 @@ public sealed class ApplicationManager : IComponentPlugin, IExecutableElements, /// private ApplicationDictionary Dictionary { get; } = new ApplicationDictionary(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Returns the stored applications. /// @@ -45,8 +50,11 @@ public sealed class ApplicationManager : IComponentPlugin, IExecutableElements, /// /// Initializes a new instance of the class. /// - internal ApplicationManager() + /// The component manager. + internal ApplicationManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index 0104021..8ae0327 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -21,32 +21,32 @@ namespace WebExpress.WebCore.WebComponent /// /// Central management of components. /// - public static class ComponentManager + public class ComponentManager { /// /// An event that fires when an component is added. /// - public static event EventHandler AddComponent; + public event EventHandler AddComponent; /// /// An event that fires when an component is removed. /// - public static event EventHandler RemoveComponent; + public event EventHandler RemoveComponent; /// /// Returns the reference to the context of the host. /// - public static IHttpServerContext HttpServerContext { get; private set; } + public IHttpServerContext HttpServerContext { get; private set; } /// /// Returns the directory where the components are listed. /// - private static ComponentDictionary Dictionary { get; } = []; + private ComponentDictionary Dictionary { get; } = []; /// /// Returns all registered components. /// - public static IEnumerable Components => new IComponent[] + public IEnumerable Components => new IComponent[] { LogManager, PackageManager, @@ -66,85 +66,85 @@ public static class ComponentManager /// Returns the log manager. /// /// The instance of the log manager or null. - public static LogManager LogManager { get; private set; } + public LogManager LogManager { get; private set; } /// /// Returns the package manager. /// /// The instance of the package manager or null. - public static PackageManager PackageManager { get; private set; } + public PackageManager PackageManager { get; private set; } /// /// Returns the plugin manager. /// /// The instance of the plugin manager or null. - public static PluginManager PluginManager { get; private set; } + public PluginManager PluginManager { get; private set; } /// /// Returns the application manager. /// /// The instance of the application manager or null. - public static ApplicationManager ApplicationManager { get; private set; } + public ApplicationManager ApplicationManager { get; private set; } /// /// Returns the module manager. /// /// The instance of the module manager or null. - public static ModuleManager ModuleManager { get; private set; } + public ModuleManager ModuleManager { get; private set; } /// /// Returns the event manager. /// /// The instance of the event manager or null. - public static EventManager EventManager { get; private set; } + public EventManager EventManager { get; private set; } /// /// Returns the job manager. /// /// The instance of the job manager or null. - public static JobManager JobManager { get; private set; } + public JobManager JobManager { get; private set; } /// /// Returns the status page manager. /// /// The instance of the status page manager or null. - public static StatusPageManager StatusPageManager { get; private set; } + public StatusPageManager StatusPageManager { get; private set; } /// /// Returns the resource manager. /// /// The instance of the resource manager or null. - public static ResourceManager ResourceManager { get; private set; } + public ResourceManager ResourceManager { get; private set; } /// /// Returns the sitemap manager. /// /// The instance of the sitemap manager or null. - public static SitemapManager SitemapManager { get; private set; } + public SitemapManager SitemapManager { get; private set; } /// /// Returns the internationalization manager. /// /// The instance of the internationalization manager or null. - public static InternationalizationManager InternationalizationManager { get; private set; } + public InternationalizationManager InternationalizationManager { get; private set; } /// /// Returns the session manager. /// /// The instance of the session manager or null. - public static SessionManager SessionManager { get; private set; } + public SessionManager SessionManager { get; private set; } /// /// Returns the task manager. /// /// The instance of the task manager manager or null. - public static TaskManager TaskManager { get; private set; } + public TaskManager TaskManager { get; private set; } /// - /// Initialization + /// Initializes a new instance of the class. /// /// The reference to the context of the host. - internal static void Initialization(IHttpServerContext httpServerContext) + internal ComponentManager(IHttpServerContext httpServerContext) { HttpServerContext = httpServerContext; @@ -186,7 +186,7 @@ internal static void Initialization(IHttpServerContext httpServerContext) /// /// The component class. /// The instance of the create and initialized component. - private static IComponent CreateInstance(Type componentType) + private IComponent CreateInstance(Type componentType) { if (componentType == null) { @@ -209,20 +209,34 @@ private static IComponent CreateInstance(Type componentType) try { var flags = BindingFlags.NonPublic | BindingFlags.Instance; - var component = componentType?.Assembly.CreateInstance - ( - componentType?.FullName, - false, - flags, - null, - null, - null, - null - ) as IComponent; - - component.Initialization(HttpServerContext); - - return component; + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors) + { + // injection + var parameters = constructor.GetParameters(); + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(ComponentManager) ? this : + parameter.ParameterType == typeof(IHttpServerContext) ? HttpServerContext : + GetType().GetProperty + ( + parameter.Name, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance + )?.GetValue(this) ?? null + ).ToArray(); + + var component = constructor.Invoke(parameterValues) as IComponent; + + if (component != null) + { + component.Initialization(HttpServerContext); + + return component; + } + } + } } catch (Exception ex) { @@ -237,7 +251,7 @@ private static IComponent CreateInstance(Type componentType) /// /// The id. /// The instance of the component or null. - public static IComponent GetComponent(string id) + public IComponent GetComponent(string id) { return Dictionary.Values .SelectMany(x => x) @@ -251,7 +265,7 @@ public static IComponent GetComponent(string id) /// /// The component class. /// The instance of the component or null. - public static T GetComponent() where T : IComponent + public T GetComponent() where T : IComponent { return (T)Dictionary.Values .SelectMany(x => x) @@ -264,7 +278,7 @@ public static T GetComponent() where T : IComponent /// Discovers and registers the components from the specified plugin. /// /// A plugin context that contain the components. - internal static void Register(IPluginContext pluginContext) + internal void Register(IPluginContext pluginContext) { // the plugin has already been registered if (Dictionary.ContainsKey(pluginContext)) @@ -315,7 +329,7 @@ internal static void Register(IPluginContext pluginContext) /// Discovers and registers the components from the specified plugins. /// /// A list with plugin contexts that contain the components. - public static void Register(IEnumerable pluginContexts) + public void Register(IEnumerable pluginContexts) { foreach (var pluinContext in pluginContexts) { @@ -327,7 +341,7 @@ public static void Register(IEnumerable pluginContexts) /// Boots the components. /// /// The plugin context. - internal static void BootComponent(IPluginContext pluginContext) + internal void BootComponent(IPluginContext pluginContext) { PluginManager.Boot(pluginContext); ApplicationManager.Boot(pluginContext); @@ -345,7 +359,7 @@ internal static void BootComponent(IPluginContext pluginContext) /// Boots the components. /// /// A enumeration of plugin contexts. - internal static void BootComponent(IEnumerable pluginContexts) + internal void BootComponent(IEnumerable pluginContexts) { foreach (var pluginContext in pluginContexts) { @@ -356,7 +370,7 @@ internal static void BootComponent(IEnumerable pluginContexts) /// /// Starts the component. /// - internal static void Execute() + internal void Execute() { HttpServerContext.Log.Debug ( @@ -370,7 +384,7 @@ internal static void Execute() /// /// Shutting down the component manager. /// - internal static void ShutDown() + internal void ShutDown() { HttpServerContext.Log.Debug ( @@ -382,7 +396,7 @@ internal static void ShutDown() /// Shutting down the component. /// /// The plugin context. - internal static void ShutDownComponent(IPluginContext pluginContext) + internal void ShutDownComponent(IPluginContext pluginContext) { PluginManager.ShutDown(pluginContext); ApplicationManager.ShutDown(pluginContext); @@ -400,7 +414,7 @@ internal static void ShutDownComponent(IPluginContext pluginContext) /// Removes all components associated with the specified plugin context. /// /// The context of the plugin that contains the applications to remove. - public static void Remove(IPluginContext pluginContext) + public void Remove(IPluginContext pluginContext) { if (pluginContext == null) { @@ -440,7 +454,7 @@ public static void Remove(IPluginContext pluginContext) /// Raises the AddComponent event. /// /// The component. - private static void OnAddComponent(IComponent component) + private void OnAddComponent(IComponent component) { AddComponent?.Invoke(null, component); } @@ -449,7 +463,7 @@ private static void OnAddComponent(IComponent component) /// Raises the RemoveComponent event. /// /// The component. - private static void OnRemoveComponent(IComponent component) + private void OnRemoveComponent(IComponent component) { RemoveComponent?.Invoke(null, component); } @@ -457,7 +471,7 @@ private static void OnRemoveComponent(IComponent component) /// /// Output of the components to the log. /// - internal static void LogStatus() + internal void LogStatus() { using var frame = new LogFrameSimple(HttpServerContext.Log); var output = new List diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 5fb709c..41827de 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -23,11 +23,19 @@ public sealed class EventManager : IComponentPlugin, ISystemComponent /// private EventDictionary Dictionary { get; } = new EventDictionary(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Initializes a new instance of the class. /// - internal EventManager() + /// The component manager. + internal EventManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 4e7e55d..4927b30 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; @@ -7,8 +8,19 @@ using System.Xml.Serialization; using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPackage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebStatusPage; +using WebExpress.WebCore.WebTask; using WebExpress.WebCore.WebUri; [assembly: InternalsVisibleTo("WebExpress.WebCore.Test")] @@ -27,11 +39,122 @@ public class WebEx /// private HttpServer HttpServer { get; set; } + /// + /// Returns the component manager. + /// + public static ComponentManager ComponentManager { get; private set; } + /// /// Returns the program version. /// public static string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(); + /// + /// An event that fires when an component is added. + /// + public static event EventHandler AddComponent + { + add { ComponentManager.AddComponent += value; } + remove { ComponentManager.AddComponent -= value; } + } + + /// + /// An event that fires when an component is removed. + /// + public static event EventHandler RemoveComponent + { + add { ComponentManager.RemoveComponent += value; } + remove { ComponentManager.RemoveComponent -= value; } + } + + /// + /// Returns the reference to the context of the host. + /// + public static IHttpServerContext HttpServerContext => ComponentManager.HttpServerContext; + + /// + /// Returns all registered components. + /// + public static IEnumerable Components => ComponentManager.Components; + + /// + /// Returns the log manager. + /// + /// The instance of the log manager or null. + public static LogManager LogManager => ComponentManager.LogManager; + + /// + /// Returns the package manager. + /// + /// The instance of the package manager or null. + public static PackageManager PackageManager => ComponentManager.PackageManager; + + /// + /// Returns the plugin manager. + /// + /// The instance of the plugin manager or null. + public static PluginManager PluginManager => ComponentManager.PluginManager; + + /// + /// Returns the application manager. + /// + /// The instance of the application manager or null. + public static ApplicationManager ApplicationManager => ComponentManager.ApplicationManager; + + /// + /// Returns the module manager. + /// + /// The instance of the module manager or null. + public static ModuleManager ModuleManager => ComponentManager.ModuleManager; + + /// + /// Returns the event manager. + /// + /// The instance of the event manager or null. + public static EventManager EventManager => ComponentManager.EventManager; + + /// + /// Returns the job manager. + /// + /// The instance of the job manager or null. + public static JobManager JobManager => ComponentManager.JobManager; + + /// + /// Returns the status page manager. + /// + /// The instance of the status page manager or null. + public static StatusPageManager StatusPageManager => ComponentManager.StatusPageManager; + + /// + /// Returns the resource manager. + /// + /// The instance of the resource manager or null. + public static ResourceManager ResourceManager => ComponentManager.ResourceManager; + + /// + /// Returns the sitemap manager. + /// + /// The instance of the sitemap manager or null. + public static SitemapManager SitemapManager => ComponentManager.SitemapManager; + + /// + /// Returns the internationalization manager. + /// + /// The instance of the internationalization manager or null. + public static InternationalizationManager InternationalizationManager => ComponentManager.InternationalizationManager; + + /// + /// Returns the session manager. + /// + /// The instance of the session manager or null. + public static SessionManager SessionManager => ComponentManager.SessionManager; + + /// + /// Returns the task manager. + /// + /// The instance of the task manager manager or null. + public static TaskManager TaskManager => ComponentManager.TaskManager; + /// /// Entry point of application. /// @@ -104,7 +227,7 @@ public int Execution(string[] args) return 1; } - WebPackage.PackageBuilder.Create(argumentDict["spec"], argumentDict["config"], argumentDict["target"], argumentDict["output"]); + PackageBuilder.Create(argumentDict["spec"], argumentDict["config"], argumentDict["target"], argumentDict["output"]); return 0; } @@ -208,6 +331,8 @@ private void Initialization(string args, string configFile) Config = config }; + ComponentManager = HttpServer.ComponentManager; + // start logging HttpServer.HttpServerContext.Log.Begin(config.Log); @@ -274,8 +399,31 @@ private void Exit() HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.done")); HttpServer.HttpServerContext.Log.Seperator('/'); + // Stop running + ComponentManager.ShutDown(); + // stop logging HttpServer.HttpServerContext.Log.Close(); } + + /// + /// Returns a component based on its id. + /// + /// The id. + /// The instance of the component or null. + public static IComponent GetComponent(string id) + { + return ComponentManager.GetComponent(id); + } + + /// + /// Returns a component based on its type. + /// + /// The component class. + /// The instance of the component or null. + public static T GetComponent() where T : IComponent + { + return ComponentManager.GetComponent(); + } } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index cb4b9d5..f571b8c 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -41,11 +41,19 @@ public sealed class JobManager : IComponentPlugin, ISystemComponent, IExecutable /// private IEnumerable DynamicScheduleList { get; set; } = new List(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Initializes a new instance of the class. /// - internal JobManager() + /// The component manager. + internal JobManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebMessage/HttpContext.cs b/src/WebExpress.WebCore/WebMessage/HttpContext.cs index 62a17e6..8191610 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpContext.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Text; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebMessage { @@ -61,7 +62,8 @@ internal HttpContext() /// /// Initial set of features. /// The context of the Web server. - public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext serverContext) + /// The component manager. + public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext serverContext, ComponentManager componentManager) { var connectionFeature = contextFeatures.Get(); var requestFeature = contextFeatures.Get(); @@ -76,7 +78,7 @@ public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext server Encoding = requestFeature.Headers.ContentEncoding.Any() ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; Uri = new Uri(baseUri, requestFeature.RawTarget); - Request = new Request(contextFeatures, serverContext, header); + Request = new Request(contextFeatures, header, componentManager); } } } diff --git a/src/WebExpress.WebCore/WebMessage/ParameterDictionary.cs b/src/WebExpress.WebCore/WebMessage/ParameterDictionary.cs index b4ba297..cb515fe 100644 --- a/src/WebExpress.WebCore/WebMessage/ParameterDictionary.cs +++ b/src/WebExpress.WebCore/WebMessage/ParameterDictionary.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Concurrent; namespace WebExpress.WebCore.WebMessage { /// /// Management of parameters. /// - public class ParameterDictionary : Dictionary + public class ParameterDictionary : ConcurrentDictionary { } } diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index 83e3cf7..ec5f6e4 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -39,7 +39,12 @@ public class Request /// /// Returns the parameters. /// - private ParameterDictionary Param { get; } = new ParameterDictionary(); + private ParameterDictionary Param { get; } = []; + + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } /// /// Returns the session. @@ -132,18 +137,19 @@ public CultureInfo Culture /// Initializes a new instance of the class. /// /// Initial set of features. - /// The context of the web server. /// The header. - internal Request(IFeatureCollection contextFeatures, IHttpServerContext serverContext, RequestHeaderFields header) + /// The component manager. + internal Request(IFeatureCollection contextFeatures, RequestHeaderFields header, ComponentManager componentManager) { var connectionFeature = contextFeatures.Get(); var requestFeature = contextFeatures.Get(); var requestIdentifierFeature = contextFeatures.Get(); //var sessionFeature = contextFeatures.Get(); - ServerContext = serverContext; + ServerContext = componentManager.HttpServerContext; RequestTraceIdentifier = requestIdentifierFeature.TraceIdentifier; Protocoll = requestFeature.Protocol; + ComponentManager = componentManager; Scheme = requestFeature.Scheme.ToLower() switch { @@ -250,7 +256,6 @@ private void ParseRequestParams() } var contentType = Header.ContentType?.Split(';'); - //var contentStr = Encoding.UTF8.GetString(Content); switch (TypeEnctypeExtensions.Convert(contentType.FirstOrDefault())) { @@ -260,7 +265,7 @@ private void ParseRequestParams() var boundaryValue = "--" + boundary?.Split('=').Skip(1)?.FirstOrDefault(); var offset = 0; int pos = 0; - var dispositions = new List>(); // Item1=Position, Item2=Länge + var dispositions = new List>(); // Item1=position, Item2=size // determine dispositions for (var i = 0; i < Content.Length; i++) @@ -481,13 +486,11 @@ public void AddParameter(IEnumerable param) /// The parameter. public void AddParameter(Parameter param) { - if (!Param.ContainsKey(param.Key.ToLower())) - { - Param.Add(param.Key.ToLower(), param); - } - else + var key = param.Key.ToLower(); + + if (!Param.TryAdd(key, param)) { - Param[param.Key.ToLower()] = param; + Param[key] = param; } } diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index a378f9d..248ccfd 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -35,6 +35,11 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst /// private ModuleDictionary Dictionary { get; } = new ModuleDictionary(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Delivers all stored modules. /// @@ -46,8 +51,11 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst /// /// Initializes a new instance of the class. /// - internal ModuleManager() + /// The component manager. + internal ModuleManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); @@ -160,7 +168,7 @@ public void Register(IPluginContext pluginContext) ); } - Dictionary.Add(pluginContext, new Dictionary()); + Dictionary.TryAdd(pluginContext, new Dictionary()); var item = Dictionary[pluginContext]; if (!item.ContainsKey(id)) @@ -268,6 +276,26 @@ private void DetachFromApplication(IApplicationContext applicationContext) } } + /// + /// Determines the module for a given application context and module id. + /// + /// The type of the application. + /// The type of the module. + /// The context of the module or null. + public IModuleContext GetModule(Type applicationType, Type moduleType) + { + var applicationContext = ComponentManager.ApplicationManager.GetApplcation(applicationType); + var item = Dictionary.Values + .SelectMany(x => x.Values) + .Where(x => x.Dictionary.ContainsKey(applicationContext)) + .Where(x => x.ModuleClass.Equals(moduleType)) + .Select(x => x.Dictionary[applicationContext]) + .Select(x => x.ModuleContext) + .FirstOrDefault(); + + return item; + } + /// /// Determines the module for a given application context and module id. /// diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index f142ea4..afef9f6 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -46,12 +46,18 @@ public sealed class PackageManager : IComponent, ISystemComponent /// private PackageCatalog Catalog { get; } = new PackageCatalog(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Initializes a new instance of the class. /// - internal PackageManager() + /// The component manager. + internal PackageManager(ComponentManager componentManager) { - + ComponentManager = componentManager; } /// diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index b5dd7f9..c598767 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -15,7 +15,7 @@ namespace WebExpress.WebCore.WebPlugin /// /// The plugin manager manages the WebExpress plugins. /// - public class PluginManager : IComponent, IExecutableElements, ISystemComponent + public sealed class PluginManager : IComponent, IExecutableElements, ISystemComponent { /// /// An event that fires when an plugin is added. @@ -32,6 +32,11 @@ public class PluginManager : IComponent, IExecutableElements, ISystemComponent /// public IHttpServerContext HttpServerContext { get; private set; } + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Returns the directory where the plugins are listed. /// @@ -50,8 +55,11 @@ public class PluginManager : IComponent, IExecutableElements, ISystemComponent /// /// Initializes a new instance of the class. /// - internal PluginManager() + /// The component manager. + internal PluginManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.AddComponent += (s, e) => { //AssignToComponent(e); diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index bcc73ff..ecdf401 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -52,7 +51,7 @@ public class ResourceContext : IResourceContext /// /// Returns the parent or null if not used. /// - public IResourceContext ParentContext => ComponentManager.ResourceManager.Resources + public IResourceContext ParentContext => WebEx.ComponentManager.ResourceManager.Resources .Where(x => !string.IsNullOrWhiteSpace(ResourceItem.ParentId)) .Where(x => x.ResourceId.Equals(ResourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 89d0e97..b1a957b 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -33,6 +33,11 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent /// public IHttpServerContext HttpServerContext { get; private set; } + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Returns the directory where the resources are listed. /// @@ -53,8 +58,11 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent /// /// Initializes a new instance of the class. /// - internal ResourceManager() + /// The component manager. + internal ResourceManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index e0e2032..0562357 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -29,6 +29,11 @@ public sealed class SitemapManager : IComponent, ISystemComponent /// private SitemapNode Root { get; set; } = new SitemapNode(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Returns the side map. /// @@ -37,9 +42,10 @@ public sealed class SitemapManager : IComponent, ISystemComponent /// /// Initializes a new instance of the class. /// - internal SitemapManager() + /// The component manager. + internal SitemapManager(ComponentManager componentManager) { - + ComponentManager = componentManager; } /// diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index c500b2d..cb0f0a6 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -40,6 +40,11 @@ public class StatusPageManager : IComponentPlugin, ISystemComponent /// private StatusPageDictionaryItem Defaults { get; } = new StatusPageDictionaryItem(); + /// + /// Returns or sets the component manager. + /// + private ComponentManager ComponentManager { get; set; } + /// /// Returns all status pages. /// @@ -57,8 +62,11 @@ public class StatusPageManager : IComponentPlugin, ISystemComponent /// /// Initializes a new instance of the class. /// - internal StatusPageManager() + /// The component manager. + internal StatusPageManager(ComponentManager componentManager) { + ComponentManager = componentManager; + ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); diff --git a/src/WebExpress.WebCore/WebTask/Task.cs b/src/WebExpress.WebCore/WebTask/Task.cs index 8f84910..c30444c 100644 --- a/src/WebExpress.WebCore/WebTask/Task.cs +++ b/src/WebExpress.WebCore/WebTask/Task.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading; -using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebTask { @@ -98,7 +97,7 @@ public void Run() OnFinish(); - ComponentManager.TaskManager.RemoveTask(this); + WebEx.ComponentManager.TaskManager.RemoveTask(this); }), TokenSource.Token); } @@ -112,7 +111,7 @@ public void Cancel() State = TaskState.Canceled; - ComponentManager.TaskManager.RemoveTask(this); + WebEx.ComponentManager.TaskManager.RemoveTask(this); } } } From 81db1ef2211537acd0288f31ed64dff1a30685c8 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Sep 2024 23:07:27 +0200 Subject: [PATCH 018/162] refactor: transition to constructor-based initialization and dependency injection - removed initialization methods in favor of constructor-based initialization. - manager and component constructor calls are now filled via dependency injection. - updated the concept by redefining components: components no longer refer to managers but to each object managed by a manager (e.g., modules). --- .../Fixture/ResourceCounter.cs | 49 +++ .../Fixture/ResourceMonitor.cs | 51 +++ .../Fixture/UnitTestControlFixture.cs | 58 +++- .../Manager/UnitTestApplication.cs | 90 +++-- .../Manager/UnitTestComponent.cs | 50 +++ .../Manager/UnitTestInternationalization.cs | 36 +- .../Manager/UnitTestModule.cs | 108 ++++-- .../Manager/UnitTestPlugin.cs | 75 +++-- .../Manager/UnitTestResourceManager.cs | 115 +++++++ .../Manager/UnitTestSitemapManager.cs | 84 +++++ .../TestApplicationA.cs | 16 + .../TestApplicationB.cs | 8 +- .../TestApplicationC.cs | 19 +- src/WebExpress.WebCore.Test/TestModuleA1.cs | 15 +- src/WebExpress.WebCore.Test/TestModuleA2.cs | 13 +- src/WebExpress.WebCore.Test/TestModuleAB1.cs | 51 +++ src/WebExpress.WebCore.Test/TestModuleB1.cs | 50 +++ src/WebExpress.WebCore.Test/TestModuleC1.cs | 50 +++ src/WebExpress.WebCore.Test/TestModuleC2.cs | 47 +++ src/WebExpress.WebCore.Test/TestPage.cs | 12 +- src/WebExpress.WebCore.Test/TestPlugin.cs | 10 +- .../TestResourceA1X.cs | 69 ++++ .../TestResourceA1Y.cs | 70 ++++ .../TestResourceA2X.cs | 76 +++++ .../TestResourceAB1X.cs | 75 +++++ src/WebExpress.WebCore/HttpServer.cs | 26 +- .../Internationalization/I18N.cs | 126 +++++++ .../IInternationalizationManager.cs | 97 ++++++ .../InternationalizationExtensions.cs | 12 +- .../InternationalizationManager.cs | 125 ++++--- .../WebApplication/ApplicationContext.cs | 2 +- .../WebApplication/ApplicationManager.cs | 129 +++---- .../WebApplication/IApplication.cs | 9 +- .../WebApplication/IApplicationContext.cs | 3 +- .../WebApplication/IApplicationManager.cs | 56 +++ .../WebAttribute/IdAttribute.cs | 2 +- .../WebComponent/ComponentActivator.cs | 91 +++++ .../WebComponent/ComponentItem.cs | 2 +- .../WebComponent/ComponentManager.cs | 178 +++++----- .../WebComponent/IComponent.cs | 24 +- .../WebComponent/IComponentManager.cs | 135 ++++++++ .../WebComponent/IContext.cs | 9 + .../WebComponent/IManager.cs | 9 + ...{IComponentPlugin.cs => IManagerPlugin.cs} | 2 +- .../WebEvent/EventManager.cs | 57 ++-- src/WebExpress.WebCore/WebEx.cs | 137 ++++---- src/WebExpress.WebCore/WebJob/Cron.cs | 8 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 135 +++----- src/WebExpress.WebCore/WebLog/LogManager.cs | 6 +- .../WebMessage/HttpContext.cs | 10 +- .../WebMessage/Parameter.cs | 7 +- src/WebExpress.WebCore/WebMessage/Request.cs | 19 +- src/WebExpress.WebCore/WebModule/IModule.cs | 12 +- .../WebModule/IModuleManager.cs | 69 ++++ .../WebModule/ModuleContext.cs | 2 +- .../WebModule/ModuleItem.cs | 17 +- .../WebModule/ModuleManager.cs | 101 +++--- .../WebPackage/PackageManager.cs | 52 ++- src/WebExpress.WebCore/WebPage/VisualTree.cs | 2 +- src/WebExpress.WebCore/WebPlugin/IPlugin.cs | 9 +- .../WebPlugin/IPluginContext.cs | 3 +- .../WebPlugin/IPluginManager.cs | 41 +++ .../WebPlugin/PluginManager.cs | 169 ++++------ .../WebResource/IResource.cs | 10 +- .../WebResource/IResourceContext.cs | 13 +- .../WebResource/IResourceManager.cs | 92 +++++ .../WebResource/Resource.cs | 5 - .../WebResource/ResourceAsset.cs | 2 +- .../WebResource/ResourceContext.cs | 43 ++- .../WebResource/ResourceFile.cs | 2 +- .../WebResource/ResourceItem.cs | 19 +- .../WebResource/ResourceManager.cs | 214 ++++++++---- .../WebSession/SessionManager.cs | 4 +- .../WebSitemap/ISitemapManager.cs | 66 ++++ .../WebSitemap/SearchResult.cs | 19 +- .../WebSitemap/SitemapManager.cs | 318 +++++++++++------- .../WebSitemap/SitemapNode.cs | 24 -- .../WebStatusPage/StatusPageManager.cs | 102 +++--- src/WebExpress.WebCore/WebTask/TaskManager.cs | 4 +- .../WebUri/UriPathSegmentConstant.cs | 2 +- .../WebUri/UriPathSegmentRoot.cs | 2 +- .../WebUri/UriPathSegmentVariable.cs | 2 +- .../WebUri/UriPathSegmentVariableGuid.cs | 2 +- 83 files changed, 2921 insertions(+), 1214 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs create mode 100644 src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestModuleAB1.cs create mode 100644 src/WebExpress.WebCore.Test/TestModuleB1.cs create mode 100644 src/WebExpress.WebCore.Test/TestModuleC1.cs create mode 100644 src/WebExpress.WebCore.Test/TestModuleC2.cs create mode 100644 src/WebExpress.WebCore.Test/TestResourceA1X.cs create mode 100644 src/WebExpress.WebCore.Test/TestResourceA1Y.cs create mode 100644 src/WebExpress.WebCore.Test/TestResourceA2X.cs create mode 100644 src/WebExpress.WebCore.Test/TestResourceAB1X.cs create mode 100644 src/WebExpress.WebCore/Internationalization/I18N.cs create mode 100644 src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs create mode 100644 src/WebExpress.WebCore/WebApplication/IApplicationManager.cs create mode 100644 src/WebExpress.WebCore/WebComponent/ComponentActivator.cs create mode 100644 src/WebExpress.WebCore/WebComponent/IComponentManager.cs create mode 100644 src/WebExpress.WebCore/WebComponent/IContext.cs create mode 100644 src/WebExpress.WebCore/WebComponent/IManager.cs rename src/WebExpress.WebCore/WebComponent/{IComponentPlugin.cs => IManagerPlugin.cs} (95%) create mode 100644 src/WebExpress.WebCore/WebModule/IModuleManager.cs create mode 100644 src/WebExpress.WebCore/WebPlugin/IPluginManager.cs create mode 100644 src/WebExpress.WebCore/WebResource/IResourceManager.cs create mode 100644 src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs b/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs new file mode 100644 index 0000000..4c6de61 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs @@ -0,0 +1,49 @@ +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test.Fixture +{ + /// + /// The class manages a list of objects. + /// + internal static class ResourceCounter + { + private static List _resources = new List(); + + /// + /// Returns the number of resources. + /// + public static int Count => ResourceCounter.Resources.Count(); + + /// + /// Returns the component manager. + /// + public static IEnumerable Resources => _resources; + + /// + /// Adds a resource to the monitor. + /// + /// The resource to be added. + public static void Add(IResource resource) + { + _resources.Add(resource); + } + + /// + /// Checks if a resource of the same type exists. + /// + /// The type of resource to check for. + /// true if a resource of the same type exists; otherwise, false. + public static bool Contains() where T : IResource + { + return _resources.Any(resource => resource is T); + } + + /// + /// Clears the list of resources. + /// + public static void Clear() + { + _resources.Clear(); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs b/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs new file mode 100644 index 0000000..566d92d --- /dev/null +++ b/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs @@ -0,0 +1,51 @@ +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test.Fixture +{ + /// + /// The class manages the component managers and disposes them when the using statement is exited. + /// + internal class ResourceMonitor : IDisposable + { + /// + /// Returns the number of resources. + /// + public int Count => ResourceCounter.Resources.Count(); + + /// + /// Initializes a new instance of the class with the specified component manager. + /// + public ResourceMonitor() + { + ResourceCounter.Clear(); + } + + /// + /// Checks if a resource of the same type exists. + /// + /// The type of resource to check for. + /// true if a resource of the same type exists; otherwise, false. + public bool Contains(Type type) + { + return ResourceCounter.Resources.Any(resource => resource.Equals(type)); + } + + /// + /// Checks if a resource of the same type exists. + /// + /// The type of resource to check for. + /// true if a resource of the same type exists; otherwise, false. + public bool Contains() where T : IResource + { + return ResourceCounter.Resources.Any(resource => resource is T); + } + + /// + /// Disposes all resources. + /// + public void Dispose() + { + ResourceCounter.Clear(); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 062dc92..cea31d7 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -9,6 +9,7 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; namespace WebExpress.WebCore.Test.Fixture @@ -16,9 +17,9 @@ namespace WebExpress.WebCore.Test.Fixture public class UnitTestControlFixture : IDisposable { /// - /// Returns a guard to protect against concurrent access. + /// Returns the /// - private static object guard = new object(); + private static List Ressources { get; } = new List(); /// /// Initializes a new instance of the class and boot the component manager. @@ -31,7 +32,7 @@ public UnitTestControlFixture() /// Create a a server context. /// /// The server context. - public static HttpServerContext CreateHttpServerContext() + public static IHttpServerContext CreateHttpServerContext() { return new HttpServerContext ( @@ -64,6 +65,26 @@ public static ComponentManager CreateComponentManager() var componentManager = (ComponentManager)ctorComponentManager.Invoke([CreateHttpServerContext()]); + // set static field in the webex class + var type = typeof(WebEx); + var field = type.GetField("_componentManager", BindingFlags.Static | BindingFlags.NonPublic); + + field.SetValue(null, componentManager); + + return componentManager; + } + + /// + /// Create a component manager. + /// + /// The component manager. + public static ComponentManager CreateAndRegisterComponentManager() + { + var componentManager = CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; + + pluginManager.Register(); + return componentManager; } @@ -74,13 +95,24 @@ public static ComponentManager CreateComponentManager() /// A fake request for testing. public static WebMessage.Request CrerateRequest(string content = "") { - var ctorRequestHeaderFields = typeof(RequestHeaderFields).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection)], null); - var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(ComponentManager)], null); + var context = CreateHttpContext(content); + + return context.Request; + } + + /// + /// Create a fake http context. + /// + /// The content. + /// A fake http context for testing. + public static WebMessage.HttpContext CreateHttpContext(string content = "") + { + var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); var featureCollection = new FeatureCollection(); var firstLine = content.Split('\n').FirstOrDefault(); var lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); - var pos = content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4; + var pos = content.Length > 0 ? content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4 : 0; var innerContent = pos < content.Length ? content.Substring(pos) : ""; var contentBytes = Encoding.UTF8.GetBytes(innerContent); @@ -100,10 +132,10 @@ public static WebMessage.Request CrerateRequest(string content = "") ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", ["Referer"] = "0HN50661TV8TP" }, - Body = new MemoryStream(contentBytes), - Method = firstLine.Split(' ').FirstOrDefault(), - RawTarget = firstLine.Split(' ').Skip(1).FirstOrDefault().Split('?').FirstOrDefault(), - QueryString = "?" + firstLine.Split(' ').Skip(1).FirstOrDefault().Split('?').Skip(1).FirstOrDefault(), + Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, + Method = firstLine.Split(' ')?.FirstOrDefault() ?? "GET", + RawTarget = firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.FirstOrDefault() ?? "/", + QueryString = "?" + firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.Skip(1)?.FirstOrDefault() ?? "", }; foreach (var line in filteredLines) @@ -134,11 +166,9 @@ public static WebMessage.Request CrerateRequest(string content = "") featureCollection.Set(connectionFeature); var componentManager = CreateComponentManager(); + var context = new WebMessage.HttpContext(featureCollection, componentManager.HttpServerContext); - var headers = (RequestHeaderFields)ctorRequestHeaderFields.Invoke([featureCollection]); - var request = (WebMessage.Request)ctorRequest.Invoke([featureCollection, headers, componentManager]); - - return request; + return context; } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index 0ad52f1..84e88ac 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -1,4 +1,6 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager { @@ -16,14 +18,15 @@ public void Register() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; // test execution - componentManager.PluginManager.Register(); + pluginManager.Register(); Assert.Equal(3, componentManager.ApplicationManager.Applications.Count()); Assert.Equal("webexpress.webcore.test.testapplicationa", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); Assert.Equal("webexpress.webcore.test.testapplicationb", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); - Assert.Equal("testapplicationc", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationc", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); } /// @@ -33,12 +36,12 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var applicationManager = componentManager.ApplicationManager as ApplicationManager; var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - componentManager.ApplicationManager.Remove(plugin); + applicationManager.Remove(plugin); Assert.Empty(componentManager.ApplicationManager.Applications); } @@ -49,12 +52,11 @@ public void Remove() [Theory] [InlineData(typeof(TestApplicationA), "webexpress.webcore.test.testapplicationa")] [InlineData(typeof(TestApplicationB), "webexpress.webcore.test.testapplicationb")] - [InlineData(typeof(TestApplicationC), "testapplicationc")] - public void GetId(Type applicationType, string id) + [InlineData(typeof(TestApplicationC), "webexpress.webcore.test.testapplicationc")] + public void Id(Type applicationType, string id) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution @@ -68,11 +70,10 @@ public void GetId(Type applicationType, string id) [InlineData(typeof(TestApplicationA), "TestApplicationA")] [InlineData(typeof(TestApplicationB), "TestApplicationB")] [InlineData(typeof(TestApplicationC), "TestApplicationC")] - public void GetName(Type applicationType, string name) + public void Name(Type applicationType, string name) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution @@ -86,11 +87,10 @@ public void GetName(Type applicationType, string name) [InlineData(typeof(TestApplicationA), "application.description")] [InlineData(typeof(TestApplicationB), "application.description")] [InlineData(typeof(TestApplicationC), "application.description")] - public void GetDescription(Type applicationType, string description) + public void Description(Type applicationType, string description) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution @@ -101,18 +101,68 @@ public void GetDescription(Type applicationType, string description) /// Test the icon property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationB), "/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationA), "/aca/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationB), "/acb/assets/img/Logo.png")] [InlineData(typeof(TestApplicationC), "/assets/img/Logo.png")] - public void GetIcon(Type applicationType, string icon) + public void Icon(Type applicationType, string icon) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(icon, applcation.Icon); } + + /// + /// Test the context path property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "/aca")] + [InlineData(typeof(TestApplicationB), "/acb")] + [InlineData(typeof(TestApplicationC), "/")] + public void ContextPath(Type applicationType, string contextPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + + // test execution + Assert.Equal(contextPath, applcation.ContextPath); + } + + /// + /// Test the asset path property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "/aaa")] + [InlineData(typeof(TestApplicationB), "/aab")] + [InlineData(typeof(TestApplicationC), "/")] + public void AssetPath(Type applicationType, string assetPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + + // test execution + Assert.Equal(assetPath, applcation.AssetPath); + } + + /// + /// Test the data path property of the application. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "/ada")] + [InlineData(typeof(TestApplicationB), "/adb")] + [InlineData(typeof(TestApplicationC), "/")] + public void DataPath(Type applicationType, string dataPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + + // test execution + Assert.Equal(dataPath, applcation.DataPath); + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs new file mode 100644 index 0000000..0cc7199 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.Test.Fixture; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the component manager. + /// + [Collection("NonParallelTests")] + public class UnitTestComponent + { + /// + /// Test the plugin manager property of the component manager. + /// + [Fact] + public void PluginManager() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + + // test execution + Assert.NotNull(componentManager.PluginManager); + } + + /// + /// Test the application manager property of the component manager. + /// + [Fact] + public void ApplicationManager() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + + // test execution + Assert.NotNull(componentManager.ApplicationManager); + } + + /// + /// Test the module manager property of the component manager. + /// + [Fact] + public void ModuleManager() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateComponentManager(); + + // test execution + Assert.NotNull(componentManager.ModuleManager); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 8d5aa2c..a38b473 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -1,6 +1,7 @@ using System.Globalization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager { @@ -18,11 +19,12 @@ public void Register() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; // test execution - componentManager.PluginManager.Register(); + pluginManager.Register(); - Assert.Equal("This is a test", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); + Assert.Equal("This is a test", I18N.Translate("webexpress.webcore.test:unit.test.message")); } /// @@ -32,14 +34,14 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var internationalizationManager = componentManager.InternationalizationManager as InternationalizationManager; var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - componentManager.InternationalizationManager.Remove(plugin); + internationalizationManager.Remove(plugin); - Assert.Equal("webexpress.webcore.test:unit.test.message", InternationalizationManager.I18N("webexpress.webcore.test:unit.test.message")); + Assert.Equal("webexpress.webcore.test:unit.test.message", I18N.Translate("webexpress.webcore.test:unit.test.message")); } /// @@ -49,15 +51,14 @@ public void Remove() public void GetDefaultCulture() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); // test execution Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); } /// - /// Test the I18N function of the plugin manager. + /// Test the translate function of the internationalization manager. /// [Theory] [InlineData("webexpress.webcore.test:unit.test.message", "This is a test")] @@ -67,51 +68,50 @@ public void GetDefaultCulture() [InlineData("webexpress.webcore.test:welcome.message", "Welcome 'Max' to our application!", "en", null, "Max")] [InlineData("welcome.message", "Welcome 'Max' to our application!", "en", "webexpress.webcore.test", "Max")] [InlineData("non.existent.key", "non.existent.key", "de")] - public void I18N(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) + public void Translate(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); if (cultureName == null && !param.Any()) { // test execution - var result = InternationalizationManager.I18N(key); + var result = I18N.Translate(key); Assert.Equal(excepted, result); } if (cultureName == null && param.Any()) { // test execution - var result = InternationalizationManager.I18N(key, param); + var result = I18N.Translate(key, param); Assert.Equal(excepted, result); } if (cultureName != null && pluginID == null && !param.Any()) { // test execution - var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), key); + var result = I18N.Translate(CultureInfo.GetCultureInfo(cultureName), key); Assert.Equal(excepted, result); } if (cultureName != null && pluginID == null && param.Any()) { // test execution - var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), key, param); + var result = I18N.Translate(CultureInfo.GetCultureInfo(cultureName), key, param); Assert.Equal(excepted, result); } if (cultureName != null && pluginID != null && !param.Any()) { // test execution - var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), pluginID, key); + var result = I18N.Translate(CultureInfo.GetCultureInfo(cultureName), pluginID, key); Assert.Equal(excepted, result); } if (cultureName != null && pluginID != null && param.Any()) { // test execution - var result = InternationalizationManager.I18N(CultureInfo.GetCultureInfo(cultureName), pluginID, key, param); + var result = I18N.Translate(CultureInfo.GetCultureInfo(cultureName), pluginID, key, param); Assert.Equal(excepted, result); } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs index 9bca744..fca4ad7 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -1,4 +1,6 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager { @@ -16,11 +18,12 @@ public void Register() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; // test execution - componentManager.PluginManager.Register(); + pluginManager.Register(); - Assert.Equal(2, componentManager.ModuleManager.Modules.Count()); + Assert.Equal(7, componentManager.ModuleManager.Modules.Count()); } /// @@ -30,12 +33,12 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var moduleManager = componentManager.ModuleManager as ModuleManager; var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - componentManager.ModuleManager.Remove(plugin); + moduleManager.Remove(plugin); Assert.Empty(componentManager.ModuleManager.Modules); } @@ -46,11 +49,11 @@ public void Remove() [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "webexpress.webcore.test.testmodulea1")] [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "webexpress.webcore.test.testmodulea2")] - public void GetId(Type applicationType, Type moduleType, string id) + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "webexpress.webcore.test.testmoduleb1")] + public void Id(Type applicationType, Type moduleType, string id) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -65,11 +68,11 @@ public void GetId(Type applicationType, Type moduleType, string id) [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.namea1")] [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.namea2")] - public void GetName(Type applicationType, Type moduleType, string name) + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "testmoduleb1")] + public void Name(Type applicationType, Type moduleType, string name) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -84,11 +87,11 @@ public void GetName(Type applicationType, Type moduleType, string name) [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.descriptiona1")] [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.descriptiona2")] - public void GetDescription(Type applicationType, Type moduleType, string description) + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "")] + public void Description(Type applicationType, Type moduleType, string description) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -101,19 +104,82 @@ public void GetDescription(Type applicationType, Type moduleType, string descrip /// Test the icon property of the module. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/assets/img/Logo.png")] - public void GetIcon(Type applicationType, Type moduleType, string icon) + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/aca/mca/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aca/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/acb/mcb")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/aca/mcab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/acb/mcab")] + public void Icon(Type applicationType, Type moduleType, string icon) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); // test execution + Assert.Equal(icon, module.Icon); + } + + /// + /// Test the context path property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aca")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/acb/mcb")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/aca/mcab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/acb/mcab")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mcc")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] + public void ContextPath(Type applicationType, Type moduleType, string contextPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); - Assert.Equal(icon, module.Icon); + // test execution + Assert.Equal(contextPath, module.ContextPath); + } + + /// + /// Test the asset path property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/maa")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aaa")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/mab")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/maab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/maab")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mac")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] + public void AssetPath(Type applicationType, Type moduleType, string assetPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + // test execution + Assert.Equal(assetPath, module.AssetPath); + } + + /// + /// Test the data path property of the module. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/mda")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/ada")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/mdb")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/mdab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/mdab")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mdc")] + [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] + public void DataPath(Type applicationType, Type moduleType, string dataPath) + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + // test execution + Assert.Equal(dataPath, module.DataPath); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index f30561e..1ee521d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager { @@ -16,9 +17,10 @@ public void Register() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; // test execution - componentManager.PluginManager.Register(); + pluginManager.Register(); Assert.Single(componentManager.PluginManager.Plugins); Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); @@ -32,13 +34,14 @@ public void RegisterEvent() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; var i = 0; var triggered = false; componentManager.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; // test execution - componentManager.PluginManager.Register(); + pluginManager.Register(); Assert.Single(componentManager.PluginManager.Plugins); Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); @@ -54,11 +57,12 @@ public void Remove() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var pluginManager = componentManager.PluginManager as PluginManager; + pluginManager.Register(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - componentManager.PluginManager.Remove(plugin); + pluginManager.Remove(plugin); Assert.Empty(componentManager.PluginManager.Plugins); } @@ -71,15 +75,16 @@ public void RemoveEvent() { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); + var pluginManager = componentManager.PluginManager as PluginManager; var i = 1; var triggered = false; componentManager.PluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; - componentManager.PluginManager.Register(); + pluginManager.Register(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - componentManager.PluginManager.Remove(plugin); + pluginManager.Remove(plugin); Assert.Empty(componentManager.PluginManager.Plugins); Assert.Equal(0, i); @@ -93,8 +98,7 @@ public void RemoveEvent() public void GetPluginById() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); // test execution var plugin = componentManager.PluginManager.GetPlugin("webexpress.webcore.test"); @@ -102,15 +106,29 @@ public void GetPluginById() Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } + /// + /// Test the get plugin function of the plugin manager. + /// + [Fact] + public void GetPluginByType() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + + // test execution + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + + Assert.Equal("webexpress.webcore.test", plugin?.PluginId); + } + /// /// Test the name property of the plugin. /// [Fact] - public void GetId() + public void Id() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -124,8 +142,7 @@ public void GetId() public void GetName() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -139,8 +156,7 @@ public void GetName() public void GetDescription() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -154,30 +170,13 @@ public void GetDescription() public void GetIcon() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution Assert.Equal("/assets/img/Logo.png", plugin.Icon); } - /// - /// Test the get plugin function of the plugin manager. - /// - [Fact] - public void GetPluginByType() - { - // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); - - // test execution - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); - - Assert.Equal("webexpress.webcore.test", plugin?.PluginId); - } - /// /// Test the boot function of the plugin manager. /// @@ -190,11 +189,12 @@ public void Boot(string pluginId, string expected) { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var pluginManager = componentManager.PluginManager as PluginManager; + pluginManager.Register(); var plugin = componentManager.PluginManager.GetPlugin(pluginId); // test execution - componentManager.PluginManager.Boot(plugin); + pluginManager.Boot(plugin); Assert.Single(componentManager.PluginManager.Plugins); Assert.Equal(expected, plugin?.PluginId); @@ -212,11 +212,12 @@ public void ShutDown(string pluginId, string expected) { // preconditions var componentManager = UnitTestControlFixture.CreateComponentManager(); - componentManager.PluginManager.Register(); + var pluginManager = componentManager.PluginManager as PluginManager; + pluginManager.Register(); var plugin = componentManager.PluginManager.GetPlugin(pluginId); // test execution - componentManager.PluginManager.ShutDown(plugin); + pluginManager.ShutDown(plugin); Assert.Single(componentManager.PluginManager.Plugins); Assert.Equal(expected, plugin?.PluginId); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs new file mode 100644 index 0000000..0508692 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -0,0 +1,115 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the resource manager. + /// + [Collection("NonParallelTests")] + public class UnitTestResourceManager + { + /// + /// Test the register function of the resource manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + + // test execution + // resources (3 unique + 2 ambiguous) + pages (1) + Assert.Equal(6, componentManager.ResourceManager.Resources.Count()); + } + + /// + /// Test the remove function of the resource manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var resourceManager = componentManager.ResourceManager as ResourceManager; + + // test execution + resourceManager.Remove(plugin); + + Assert.Empty(componentManager.ResourceManager.Resources); + } + + /// + /// Test the id property of the resource. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webexpress.webcore.test.testresourcea1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "webexpress.webcore.test.testresourcea1y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webexpress.webcore.test.testresourcea2x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] + + public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + using var componentMoinitor = new ResourceMonitor(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + // test execution + var resource = componentManager.ResourceManager.GetResorce(module, resourceType); + + Assert.Equal(id, resource.ResourceId); + Assert.False(componentMoinitor.Contains(resourceType)); + } + + /// + /// Test the title property of the resource. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webindex:resourcea1x.label")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "TestResourceA1Y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webindex:resourcea2x.label")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webindex:resourceab1x.label")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webindex:resourceab1x.label")] + + public void Title(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + using var componentMoinitor = new ResourceMonitor(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + // test execution + var resource = componentManager.ResourceManager.GetResorce(module, resourceType); + + Assert.Equal(id, resource.ResourceTitle); + Assert.False(componentMoinitor.Contains(resourceType)); + } + + /// + /// Test the context path property of the resource. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "/aca/mca/a1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "/aca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/aca/mcab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/acb/mcab")] + + public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + using var componentMoinitor = new ResourceMonitor(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + + // test execution + var resource = componentManager.ResourceManager.GetResorce(module, resourceType); + + Assert.Equal(id, resource.ContextPath); + Assert.False(componentMoinitor.Contains(resourceType)); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs new file mode 100644 index 0000000..56639cc --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -0,0 +1,84 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebSitemap; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the resource manager. + /// + [Collection("NonParallelTests")] + public class UnitTestSitemapManager + { + /// + /// Test the refresh function of the sitemap manager. + /// + [Fact] + public void Refresh() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + + // test execution + componentManager.SitemapManager.Refresh(); + + Assert.Equal(13, componentManager.SitemapManager.SiteMap.Count()); + } + + /// + /// Test the SearchResource function of the sitemap. + /// + [Theory] + [InlineData("http://localhost:8080/aca/mca/a1x", "webexpress.webcore.test.testresourcea1x")] + [InlineData("http://localhost:8080/aca/mca/a1x/a1y", "webexpress.webcore.test.testresourcea1y")] + [InlineData("http://localhost:8080/aca/a2x", "webexpress.webcore.test.testresourcea2x")] + [InlineData("http://localhost:8080/aca/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] + [InlineData("http://localhost:8080/acb/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] + [InlineData("http://localhost:8080/uri/does/not/exist", null)] + + public void SearchResource(string uri, string id) + { + // preconditions + using var componentMoinitor = new ResourceMonitor(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var context = UnitTestControlFixture.CreateHttpContext(); + componentManager.SitemapManager.Refresh(); + var map = componentManager.SitemapManager.ToString(); + + // test execution + var searchResult = componentManager.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() + { + HttpServerContext = componentManager.HttpServerContext, + Culture = componentManager.HttpServerContext.Culture, + HttpContext = context + }); + + Assert.Equal(id, searchResult.ResourceId); + //Assert.Single(componentMoinitor.Contains(resourceType)); + } + + /// + /// Test the GetUri function of the sitemap. + /// + [Theory] + [InlineData(typeof(TestResourceA1X), "/aca/mca/a1x")] + [InlineData(typeof(TestResourceA1Y), "/aca/mca/a1x/a1y")] + [InlineData(typeof(TestResourceA2X), "/aca/a2x")] + [InlineData(typeof(TestResourceAB1X), "/aca/mcab/ab1x")] + + public void GetUri(Type resourceType, string expected) + { + // preconditions + using var componentMoinitor = new ResourceMonitor(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var context = UnitTestControlFixture.CreateHttpContext(); + componentManager.SitemapManager.Refresh(); + //var ressource = componentManager.ResourceManager.GetResorces(resourceType).FirstOrDefault(); + + // test execution + var uri = componentManager.SitemapManager.GetUri(resourceType); + + Assert.Equal(expected, uri?.ToString()); + Assert.False(componentMoinitor.Contains(resourceType)); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestApplicationA.cs b/src/WebExpress.WebCore.Test/TestApplicationA.cs index 7dde9b4..ce03bba 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationA.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationA.cs @@ -9,9 +9,25 @@ namespace WebExpress.WebCore.Test [Name("TestApplicationA")] [Description("application.description")] [Icon("/assets/img/Logo.png")] + [ContextPath("/aca")] + [AssetPath("/aaa")] + [DataPath("/ada")] [Dependency("webexpress.webui")] public sealed class TestApplicationA : IApplication { + /// + /// Initializes a new instance of the class. + /// + /// The application context, for testing the injection. + private TestApplicationA(IApplicationContext applicationContext) + { + // test the injection + if (applicationContext == null) + { + throw new ArgumentNullException(nameof(applicationContext), "Parameter cannot be null or empty."); + } + } + /// /// Initialization of the application. /// diff --git a/src/WebExpress.WebCore.Test/TestApplicationB.cs b/src/WebExpress.WebCore.Test/TestApplicationB.cs index 5fa38d2..ffdc9ec 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationB.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationB.cs @@ -9,14 +9,16 @@ namespace WebExpress.WebCore.Test [Name("TestApplicationB")] [Description("application.description")] [Icon("/assets/img/Logo.png")] + [ContextPath("/acb")] + [AssetPath("/aab")] + [DataPath("/adb")] [Dependency("webexpress.webui")] public sealed class TestApplicationB : IApplication { /// - /// Initialization of the application. + /// Initializes a new instance of the class. /// - /// The application context. - public void Initialization(IApplicationContext applicationContext) + private TestApplicationB() { } diff --git a/src/WebExpress.WebCore.Test/TestApplicationC.cs b/src/WebExpress.WebCore.Test/TestApplicationC.cs index 06e9677..5adfc91 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationC.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationC.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test { @@ -14,11 +15,23 @@ namespace WebExpress.WebCore.Test public sealed class TestApplicationC : IApplication { /// - /// Initialization of the application. + /// Initializes a new instance of the class. /// - /// The application context. - public void Initialization(IApplicationContext applicationContext) + /// The application context, for testing the injection. + /// The plugin manager, for testing the injection. + private TestApplicationC(IApplicationContext applicationContext, IPluginManager pluginManager) { + // test the injection + if (applicationContext == null) + { + throw new ArgumentNullException(nameof(applicationContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (pluginManager == null) + { + throw new ArgumentNullException(nameof(pluginManager), "Parameter cannot be null or empty."); + } } /// diff --git a/src/WebExpress.WebCore.Test/TestModuleA1.cs b/src/WebExpress.WebCore.Test/TestModuleA1.cs index 74265a5..ee91ae4 100644 --- a/src/WebExpress.WebCore.Test/TestModuleA1.cs +++ b/src/WebExpress.WebCore.Test/TestModuleA1.cs @@ -10,17 +10,22 @@ namespace WebExpress.WebCore.Test [Name("module.namea1")] [Description("module.descriptiona1")] [Icon("/assets/img/Logo.png")] - [AssetPath("/")] - [ContextPath("/")] + [ContextPath("/mca")] + [AssetPath("/maa")] + [DataPath("/mda")] public sealed class TestModuleA1 : IModule { /// /// Initialization of the module. /// - /// The module context. - public void Initialization(IModuleContext moduleContext) + /// The module context, for testing the injection. + private TestModuleA1(IModuleContext moduleContext) { - throw new NotImplementedException(); + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } } /// diff --git a/src/WebExpress.WebCore.Test/TestModuleA2.cs b/src/WebExpress.WebCore.Test/TestModuleA2.cs index 5aed014..4a02b4a 100644 --- a/src/WebExpress.WebCore.Test/TestModuleA2.cs +++ b/src/WebExpress.WebCore.Test/TestModuleA2.cs @@ -9,18 +9,21 @@ namespace WebExpress.WebCore.Test [Application()] [Name("module.namea2")] [Description("module.descriptiona2")] + [ContextPath(null)] [Icon("/assets/img/Logo.png")] - [AssetPath("/")] - [ContextPath("/")] public sealed class TestModuleA2 : IModule { /// /// Initialization of the module. /// - /// The module context. - public void Initialization(IModuleContext moduleContext) + /// The module context, for testing the injection. + private TestModuleA2(IModuleContext moduleContext) { - throw new NotImplementedException(); + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } } /// diff --git a/src/WebExpress.WebCore.Test/TestModuleAB1.cs b/src/WebExpress.WebCore.Test/TestModuleAB1.cs new file mode 100644 index 0000000..5e5e289 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModuleAB1.cs @@ -0,0 +1,51 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + [Application()] + [ContextPath("/mcab")] + [AssetPath("/maab")] + [DataPath("/mdab")] + public sealed class TestModuleAB1 : IModule + { + /// + /// Initialization of the module. + /// + /// The module context, for testing the injection. + /// The application manager, for testing the injection. + private TestModuleAB1(IModuleContext moduleContext, ApplicationManager applicationManager) + { + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModuleB1.cs b/src/WebExpress.WebCore.Test/TestModuleB1.cs new file mode 100644 index 0000000..dee725b --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModuleB1.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + [ContextPath("/mcb")] + [AssetPath("/mab")] + [DataPath("/mdb")] + public sealed class TestModuleB1 : IModule + { + /// + /// Initialization of the module. + /// + /// The module context, for testing the injection. + /// The application manager, for testing the injection. + private TestModuleB1(IModuleContext moduleContext, ApplicationManager applicationManager) + { + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModuleC1.cs b/src/WebExpress.WebCore.Test/TestModuleC1.cs new file mode 100644 index 0000000..26436aa --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModuleC1.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + [ContextPath("/mcc")] + [AssetPath("/mac")] + [DataPath("/mdc")] + public sealed class TestModuleC1 : IModule + { + /// + /// Initialization of the module. + /// + /// The module context, for testing the injection. + /// The application manager, for testing the injection. + private TestModuleC1(IModuleContext moduleContext, ApplicationManager applicationManager) + { + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModuleC2.cs b/src/WebExpress.WebCore.Test/TestModuleC2.cs new file mode 100644 index 0000000..80ba2ec --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestModuleC2.cs @@ -0,0 +1,47 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebModule; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy module for testing purposes. + /// + [Application()] + public sealed class TestModuleC2 : IModule + { + /// + /// Initialization of the module. + /// + /// The module context, for testing the injection. + /// The application manager, for testing the injection. + private TestModuleC2(IModuleContext moduleContext, ApplicationManager applicationManager) + { + // test the injection + if (moduleContext == null) + { + throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the plugin starts working. The call is concurrent. + /// + public void Run() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPage.cs b/src/WebExpress.WebCore.Test/TestPage.cs index e99866a..cacdb23 100644 --- a/src/WebExpress.WebCore.Test/TestPage.cs +++ b/src/WebExpress.WebCore.Test/TestPage.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.ComponentModel; +using System.Globalization; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; @@ -29,6 +30,14 @@ public sealed class TestPage : IPage /// Returns or sets the culture information. /// public CultureInfo Culture { get => CultureInfo.CurrentCulture; set => throw new NotImplementedException(); } + public ISite Site { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public event EventHandler Disposed; + + public void Dispose() + { + throw new NotImplementedException(); + } /// /// Instillation of the resource. Here, for example, managed resources can be loaded. @@ -77,5 +86,6 @@ public void Redirecting(string uri) { } + } } diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs index 01c939c..7b65278 100644 --- a/src/WebExpress.WebCore.Test/TestPlugin.cs +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -11,17 +11,13 @@ namespace WebExpress.WebCore.Test [Icon("/assets/img/Logo.png")] public sealed class TestPlugin : IPlugin { - public TestPlugin() - { - - } - /// - /// Initialization of the plugin. + /// Initializes a new instance of the class. /// /// The plugin context. - public void Initialization(IPluginContext pluginContext) + private TestPlugin(IPluginContext pluginContext) { + } /// diff --git a/src/WebExpress.WebCore.Test/TestResourceA1X.cs b/src/WebExpress.WebCore.Test/TestResourceA1X.cs new file mode 100644 index 0000000..4a1683c --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestResourceA1X.cs @@ -0,0 +1,69 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourcea1x.label")] + [Segment("a1x", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestResourceA1X : IResource + { + /// + /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// + /// The context of the resource. + public TestResourceA1X(IResourceContext resourceContext) + { + // test the injection + if (resourceContext == null) + { + throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); + } + + ResourceCounter.Add(this); + } + + /// + /// Post-processes the request and response. + /// + /// The request. + /// The response. + /// The processed response. + public Response PostProcess(WebMessage.Request request, Response response) + { + return null; + } + + /// + /// Pre-processes the request. + /// + /// The request. + public void PreProcess(WebMessage.Request request) + { + + } + + /// + /// Processes the request. + /// + /// The request. + /// The processed response. + public Response Process(WebMessage.Request request) + { + return null; + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs new file mode 100644 index 0000000..612a5e3 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs @@ -0,0 +1,70 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Segment("a1y", "webindex:homepage.label")] + [Parent] + [Module] + public sealed class TestResourceA1Y : IResource + { + /// + /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// + public TestResourceA1Y() + { + ResourceCounter.Add(this); + } + + /// + /// Post-processes the request and response. + /// + /// The request. + /// The response. + /// The processed response. + public Response PostProcess(WebMessage.Request request, Response response) + { + return null; + } + + /// + /// Pre-processes the request. + /// + /// The request. + public void PreProcess(WebMessage.Request request) + { + + } + + /// + /// Processes the request. + /// + /// The request. + /// The processed response. + public Response Process(WebMessage.Request request) + { + return null; + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceA2X.cs b/src/WebExpress.WebCore.Test/TestResourceA2X.cs new file mode 100644 index 0000000..58d9be7 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestResourceA2X.cs @@ -0,0 +1,76 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourcea2x.label")] + [Segment("a2x", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestResourceA2X : IResource + { + /// + /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// + /// The resource manager. + /// The context of the resource. + public TestResourceA2X(IResourceManager resourceManager, IResourceContext resourceContext) + { + // test the injection + if (resourceManager == null) + { + throw new ArgumentNullException(nameof(resourceManager), "Parameter cannot be null or empty."); + } + + // test the injection + if (resourceContext == null) + { + throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); + } + + ResourceCounter.Add(this); + } + + /// + /// Post-processes the request and response. + /// + /// The request. + /// The response. + /// The processed response. + public Response PostProcess(WebMessage.Request request, Response response) + { + return null; + } + + /// + /// Pre-processes the request. + /// + /// The request. + public void PreProcess(WebMessage.Request request) + { + + } + + /// + /// Processes the request. + /// + /// The request. + /// The processed response. + public Response Process(WebMessage.Request request) + { + return null; + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs new file mode 100644 index 0000000..cb14cf8 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs @@ -0,0 +1,75 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebResource; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourceab1x.label")] + [Segment("ab1x", "webindex:homepage.label")] + [Module] + public sealed class TestResourceAB1X : IResource + { + /// + /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// + /// The context of the resource. + /// The resource manager. + public TestResourceAB1X(IResourceContext resourceContext, IResourceManager resourceManager) + { + // test the injection + if (resourceManager == null) + { + throw new ArgumentNullException(nameof(resourceManager), "Parameter cannot be null or empty."); + } + + // test the injection + if (resourceContext == null) + { + throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); + } + + ResourceCounter.Add(this); + } + + /// + /// Post-processes the request and response. + /// + /// The request. + /// The response. + /// The processed response. + public Response PostProcess(WebMessage.Request request, Response response) + { + return null; + } + + /// + /// Pre-processes the request. + /// + /// The request. + public void PreProcess(WebMessage.Request request) + { + + } + + /// + /// Processes the request. + /// + /// The request. + /// The processed response. + public Response Process(WebMessage.Request request) + { + return null; + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 7da2912..d366373 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -17,7 +17,6 @@ using System.Threading.Tasks; using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; @@ -73,11 +72,6 @@ public class HttpServer : IHost, II18N, IHttpApplication /// public long RequestNumber { get; private set; } - /// - /// Returns the component manager. - /// - public ComponentManager ComponentManager { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -99,8 +93,6 @@ public HttpServer(HttpServerContext context) ); Culture = HttpServerContext.Culture; - - ComponentManager = new ComponentManager(HttpServerContext); } /// @@ -255,7 +247,7 @@ private Response HandleClient(HttpContext context) var uri = request?.Uri; HttpServerContext.Log.Debug(message: this.I18N("webexpress:httpserver.connected"), args: context.RemoteEndPoint); - HttpServerContext.Log.Info(InternationalizationManager.I18N + HttpServerContext.Log.Info(I18N.Translate ( "webexpress:httpserver.request", context.RemoteEndPoint, @@ -277,8 +269,8 @@ private Response HandleClient(HttpContext context) resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count)?.PathSegments) { ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments), - ApplicationRoot = new UriResource(request.Uri, searchResult.ApplicationContext?.ContextPath.PathSegments), - ModuleRoot = new UriResource(request.Uri, searchResult.ModuleContext?.ContextPath.PathSegments), + ApplicationRoot = new UriResource(request.Uri, searchResult.ResourceContext?.ModuleContext?.ApplicationContext?.ContextPath.PathSegments), + ModuleRoot = new UriResource(request.Uri, searchResult.ResourceContext?.ModuleContext?.ContextPath.PathSegments), ResourceRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) }; @@ -368,7 +360,7 @@ private Response HandleClient(HttpContext context) stopwatch.Stop(); - HttpServerContext.Log.Info(InternationalizationManager.I18N + HttpServerContext.Log.Info(I18N.Translate ( "webexpress:httpserver.request.done", context?.RemoteEndPoint, @@ -475,12 +467,12 @@ private async Task SendResponseAsync(HttpContext context, Response response) if (searchResult != null) { - var statusPage = ComponentManager.StatusPageManager.CreateStatusPage + var statusPage = WebEx.ComponentManager.StatusPageManager.CreateStatusPage ( massage, response.Status, - searchResult?.ModuleContext?.PluginContext ?? - searchResult?.ApplicationContext?.PluginContext + searchResult?.ResourceContext?.ModuleContext?.PluginContext ?? + searchResult?.ResourceContext?.ModuleContext?.ApplicationContext?.PluginContext ); if (statusPage == null) @@ -514,7 +506,7 @@ private async Task SendResponseAsync(HttpContext context, Response response) // ContextPath = new UriResource() //}; - resource.Initialization(new ResourceContext(resource.ModuleContext)); + resource.Initialization(new ResourceContext(resource.ModuleContext, WebEx.ResourceManager)); } return statusPage.Process(request); @@ -540,7 +532,7 @@ public HttpContext CreateContext(IFeatureCollection contextFeatures) { try { - return new HttpContext(contextFeatures, HttpServerContext, ComponentManager); + return new HttpContext(contextFeatures, HttpServerContext); } catch (Exception ex) { diff --git a/src/WebExpress.WebCore/Internationalization/I18N.cs b/src/WebExpress.WebCore/Internationalization/I18N.cs new file mode 100644 index 0000000..10021a6 --- /dev/null +++ b/src/WebExpress.WebCore/Internationalization/I18N.cs @@ -0,0 +1,126 @@ +using System.Globalization; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.Internationalization +{ + /// + /// Provides internationalization (i18n) functionalities. + /// + public static class I18N + { + /// + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(string key) + { + return WebEx.InternationalizationManager?.Translate(key) ?? key; + } + + /// + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(string key, params object[] args) + { + return WebEx.InternationalizationManager?.Translate(key, args) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// An internationalization object that is being extended. + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(II18N obj, string key) + { + return WebEx.InternationalizationManager?.Translate(obj, key) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// An internationalization object that is being extended. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(II18N obj, string key, params object[] args) + { + return WebEx.InternationalizationManager?.Translate(obj, key, args) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The request with the language to use. + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(Request request, string key) + { + return WebEx.InternationalizationManager.Translate(request, key) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The request with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(Request request, string key, params object[] args) + { + return WebEx.InternationalizationManager?.Translate(request, key, args) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(CultureInfo culture, string key) + { + return WebEx.InternationalizationManager?.Translate(culture, key) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(CultureInfo culture, string key, params object[] args) + { + return WebEx.InternationalizationManager?.Translate(culture, key, args) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The plugin id. + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(CultureInfo culture, string pluginId, string key) + { + return WebEx.InternationalizationManager?.Translate(culture, pluginId, key) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The plugin id. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(CultureInfo culture, string pluginId, string key, params object[] args) + { + return WebEx.InternationalizationManager?.Translate(culture, pluginId, key, args) ?? key; + } + } +} diff --git a/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs new file mode 100644 index 0000000..58504c9 --- /dev/null +++ b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs @@ -0,0 +1,97 @@ +using System.Globalization; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.Internationalization +{ + /// + /// The interface of the internationalization manager. + /// + public interface IInternationalizationManager : IManager + { + /// + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The value of the key in the current language. + public string Translate(string key); + + /// + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public string Translate(string key, params object[] args); + + /// + /// Translates a given key to the specified language. + /// + /// An internationalization object that is being extended. + /// The internationalization key. + /// The value of the key in the current language. + string Translate(II18N obj, string key); + + /// + /// Translates a given key to the specified language. + /// + /// An internationalization object that is being extended. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + string Translate(II18N obj, string key, params object[] args); + + /// + /// Translates a given key to the specified language. + /// + /// The request with the language to use. + /// The internationalization key. + /// The value of the key in the current language. + string Translate(Request request, string key); + + /// + /// Translates a given key to the specified language. + /// + /// The request with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + string Translate(Request request, string key, params object[] args); + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The internationalization key. + /// The value of the key in the current language. + string Translate(CultureInfo culture, string key); + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + string Translate(CultureInfo culture, string key, params object[] args); + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The plugin id. + /// The internationalization key. + /// The value of the key in the current language. + string Translate(CultureInfo culture, string pluginId, string key); + + /// + /// Translates a given key to the specified language. + /// + /// The culture with the language to use. + /// The plugin id. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + string Translate(CultureInfo culture, string pluginId, string key, params object[] args); + } +} diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 3ded8b6..889ea45 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -13,7 +13,7 @@ public static class InternationalizationExtensions /// The value of the key in the current language. public static string I18N(this II18N obj, string key) { - return InternationalizationManager.I18N(obj, key); + return WebEx.InternationalizationManager.Translate(obj, key); } /// @@ -25,7 +25,7 @@ public static string I18N(this II18N obj, string key) /// The value of the key in the current language. public static string I18N(this II18N obj, string pluginId, string key) { - return InternationalizationManager.I18N(obj.Culture, pluginId, key); + return WebEx.InternationalizationManager.Translate(obj.Culture, pluginId, key); } /// @@ -37,7 +37,7 @@ public static string I18N(this II18N obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this II18N obj, IApplicationContext applicationContext, string key) { - return InternationalizationManager.I18N(obj.Culture, applicationContext?.PluginContext?.PluginId, key); + return WebEx.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); } /// @@ -49,7 +49,7 @@ public static string I18N(this II18N obj, IApplicationContext applicationContext /// The value of the key in the current language. public static string I18N(this RenderContext obj, string pluginId, string key) { - return InternationalizationManager.I18N(obj.Culture, pluginId, key); + return WebEx.InternationalizationManager.Translate(obj.Culture, pluginId, key); } /// @@ -60,7 +60,7 @@ public static string I18N(this RenderContext obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, string key) { - return InternationalizationManager.I18N(obj.Culture, obj?.PluginContext?.PluginId, key); + return WebEx.InternationalizationManager.Translate(obj.Culture, obj?.PluginContext?.PluginId, key); } /// @@ -72,7 +72,7 @@ public static string I18N(this RenderContext obj, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, IApplicationContext applicationContext, string key) { - return InternationalizationManager.I18N(obj.Culture, applicationContext?.PluginContext?.PluginId, key); + return WebEx.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); } } } diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 750aefb..c1cfdaa 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -12,8 +12,10 @@ namespace WebExpress.WebCore.Internationalization /// /// Internationalization /// - public sealed class InternationalizationManager : IComponentPlugin, ISystemComponent + public sealed class InternationalizationManager : IInternationalizationManager, IManagerPlugin, ISystemComponent { + private readonly IComponentManager _componentManager; + /// /// Returns the default language. /// @@ -29,42 +31,31 @@ public sealed class InternationalizationManager : IComponentPlugin, ISystemCompo /// public IHttpServerContext HttpServerContext { get; private set; } - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Initializes a new instance of the class. /// /// The component manager. - internal InternationalizationManager(ComponentManager componentManager) + /// The reference to the context of the host. + private InternationalizationManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + HttpServerContext = httpServerContext; DefaultCulture = HttpServerContext.Culture; HttpServerContext.Log.Debug ( - I18N("webexpress:internationalizationmanager.initialization") + Translate("webexpress:internationalizationmanager.initialization") ); } @@ -79,7 +70,7 @@ public void Register(IPluginContext pluginContext) HttpServerContext.Log.Debug ( - I18N("webexpress:internationalizationmanager.register", pluginId) + Translate("webexpress:internationalizationmanager.register", pluginId) ); } @@ -100,11 +91,11 @@ public void Register(IEnumerable pluginContexts) /// /// The assembly that contains the key-value pairs to insert. /// The id of the plugin to which the internationalization data will be assigned. - internal static void Register(Assembly assembly, string pluginId) + internal void Register(Assembly assembly, string pluginId) { var assemblyName = assembly.GetName().Name.ToLower(); var name = assemblyName + ".internationalization."; - var resources = assembly.GetManifestResourceNames().Where(x => x.ToLower().Contains(name)); + var resources = assembly.GetManifestResourceNames().Where(x => x.Contains(name, System.StringComparison.CurrentCultureIgnoreCase)); foreach (var languageResource in resources) { @@ -112,7 +103,7 @@ internal static void Register(Assembly assembly, string pluginId) if (!Dictionary.ContainsKey(language)) { - Dictionary.Add(language, new InternationalizationItem()); + Dictionary.Add(language, []); } var dictItem = Dictionary[language]; @@ -159,82 +150,103 @@ public void Remove(IPluginContext pluginContext) } /// - /// Internationalization of a key. + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The value of the key in the current language. + public string Translate(string key) + { + return Translate(DefaultCulture, null, key); + } + + /// + /// Translates a given key to the default language. + /// + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public string Translate(string key, params object[] args) + { + return string.Format(Translate(DefaultCulture, null, key), args); + } + + /// + /// Translates a given key to the specified language. /// /// An internationalization object that is being extended. /// The internationalization key. /// The value of the key in the current language. - public static string I18N(II18N obj, string key) + public string Translate(II18N obj, string key) { - return I18N(obj.Culture, key); + return Translate(obj.Culture, key); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// An internationalization object that is being extended. /// The internationalization key. /// The formatting arguments. /// The value of the key in the current language. - public static string I18N(II18N obj, string key, params object[] args) + public string Translate(II18N obj, string key, params object[] args) { - return string.Format(I18N(obj, key), args); + return string.Format(Translate(obj, key), args); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The request with the language to use. /// The internationalization key. /// The value of the key in the current language. - public static string I18N(Request request, string key) + public string Translate(Request request, string key) { - return I18N(request.Culture, null, key); + return Translate(request.Culture, null, key); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The request with the language to use. /// The internationalization key. /// The formatting arguments. /// The value of the key in the current language. - public static string I18N(Request request, string key, params object[] args) + public string Translate(Request request, string key, params object[] args) { - return string.Format(I18N(request, key), args); + return string.Format(Translate(request, key), args); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The culture with the language to use. /// The internationalization key. /// The value of the key in the current language. - public static string I18N(CultureInfo culture, string key) + public string Translate(CultureInfo culture, string key) { - return I18N(culture, null, key); + return Translate(culture, null, key); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The culture with the language to use. /// The internationalization key. /// The formatting arguments. /// The value of the key in the current language. - public static string I18N(CultureInfo culture, string key, params object[] args) + public string Translate(CultureInfo culture, string key, params object[] args) { - return string.Format(I18N(culture, key), args); + return string.Format(Translate(culture, key), args); } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The culture with the language to use. /// The plugin id. /// The internationalization key. /// The value of the key in the current language. - public static string I18N(CultureInfo culture, string pluginId, string key) + public string Translate(CultureInfo culture, string pluginId, string key) { var language = culture?.TwoLetterISOLanguageName; var k = string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(pluginId) || key.StartsWith($"{pluginId?.ToLower()}:") ? key?.ToLower() : $"{pluginId?.ToLower()}:{key?.ToLower()}"; @@ -266,37 +278,16 @@ public static string I18N(CultureInfo culture, string pluginId, string key) } /// - /// Internationalization of a key. + /// Translates a given key to the specified language. /// /// The culture with the language to use. /// The plugin id. /// The internationalization key. /// The formatting arguments. /// The value of the key in the current language. - public static string I18N(CultureInfo culture, string pluginId, string key, params object[] args) - { - return string.Format(I18N(culture, pluginId, key), args); - } - - /// - /// Internationalization of a key. - /// - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(string key) - { - return I18N(DefaultCulture, null, key); - } - - /// - /// Internationalization of a key. - /// - /// The internationalization key. - /// The formatting arguments. - /// The value of the key in the current language. - public static string I18N(string key, params object[] args) + public string Translate(CultureInfo culture, string pluginId, string key, params object[] args) { - return string.Format(I18N(DefaultCulture, null, key), args); + return string.Format(Translate(culture, pluginId, key), args); } /// diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs index 36324c5..6e6f2fc 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs @@ -64,7 +64,7 @@ public ApplicationContext() /// The string that uniquely represents the application. public override string ToString() { - return $"Application {ApplicationId}"; + return $"{ApplicationId}"; } } } diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index e2c5a55..fda630f 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -15,8 +15,12 @@ namespace WebExpress.WebCore.WebApplication /// /// Management of WebExpress applications. /// - public sealed class ApplicationManager : IComponentPlugin, IExecutableElements, ISystemComponent + public sealed class ApplicationManager : IApplicationManager, IManagerPlugin, IExecutableElements, ISystemComponent { + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly ApplicationDictionary _dictionary = []; + /// /// An event that fires when an application is added. /// @@ -27,56 +31,35 @@ public sealed class ApplicationManager : IComponentPlugin, IExecutableElements, /// public event EventHandler RemoveApplication; - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns or sets the directory where the applications are listed. - /// - private ApplicationDictionary Dictionary { get; } = new ApplicationDictionary(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Returns the stored applications. /// - public IEnumerable Applications => Dictionary.Values.SelectMany(x => x.Values).Select(x => x.ApplicationContext); + public IEnumerable Applications => _dictionary.Values.SelectMany(x => x.Values).Select(x => x.ApplicationContext); /// /// Initializes a new instance of the class. /// /// The component manager. - internal ApplicationManager(ComponentManager componentManager) + /// The reference to the context of the host. + private ApplicationManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = _componentManager.HttpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:applicationmanager.initialization") + I18N.Translate("webexpress:applicationmanager.initialization") ); } @@ -87,15 +70,15 @@ public void Initialization(IHttpServerContext context) public void Register(IPluginContext pluginContext) { // the plugin has already been registered - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.ContainsKey(pluginContext)) { return; } - Dictionary.Add(pluginContext, new Dictionary()); + _dictionary.Add(pluginContext, new Dictionary()); var assembly = pluginContext.Assembly; - var pluginDict = Dictionary[pluginContext]; + var pluginDict = _dictionary[pluginContext]; foreach (var type in assembly.GetExportedTypes().Where ( @@ -110,19 +93,15 @@ public void Register(IPluginContext pluginContext) var icon = string.Empty; var description = string.Empty; var contextPath = string.Empty; - var assetPath = Path.DirectorySeparatorChar.ToString(); - var dataPath = Path.DirectorySeparatorChar.ToString(); + var assetPath = "/"; + var dataPath = "/"; var options = new List(); // determining attributes foreach (var customAttribute in type.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IApplicationAttribute)))) { - if (customAttribute.AttributeType == typeof(IdAttribute)) - { - id = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()?.ToLower() ?? id; - } - else if (customAttribute.AttributeType == typeof(NameAttribute)) + if (customAttribute.AttributeType == typeof(NameAttribute)) { name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } @@ -173,14 +152,19 @@ public void Register(IPluginContext pluginContext) ApplicationName = name, Description = description, Options = options, - AssetPath = Path.Combine(HttpServerContext.AssetPath, assetPath), - DataPath = Path.Combine(HttpServerContext.DataPath, dataPath), - Icon = UriResource.Combine(HttpServerContext.ContextPath, contextPath, icon), - ContextPath = UriResource.Combine(HttpServerContext.ContextPath, contextPath) + AssetPath = Path.Combine(_httpServerContext.AssetPath, assetPath), + DataPath = Path.Combine(_httpServerContext.DataPath, dataPath), + Icon = UriResource.Combine(_httpServerContext.ContextPath, contextPath, icon), + ContextPath = UriResource.Combine(_httpServerContext.ContextPath, contextPath) }; // create application - var applicationInstance = Activator.CreateInstance(type) as IApplication; + var applicationInstance = ComponentActivator.CreateInstance + ( + type, + applicationContext, + _componentManager + ); if (!pluginDict.ContainsKey(id)) { @@ -191,9 +175,9 @@ public void Register(IPluginContext pluginContext) Application = applicationInstance }); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:applicationmanager.register", id) + I18N.Translate("webexpress:applicationmanager.register", id) ); // raises the AddApplication event @@ -201,9 +185,9 @@ public void Register(IPluginContext pluginContext) } else { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N("webexpress:applicationmanager.duplicate", id) + I18N.Translate("webexpress:applicationmanager.duplicate", id) ); } } @@ -230,7 +214,7 @@ public IApplicationContext GetApplcation(string applicationId) { if (string.IsNullOrWhiteSpace(applicationId)) return null; - var items = Dictionary.Values + var items = _dictionary.Values .Where(x => x.ContainsKey(applicationId.ToLower())) .Select(x => x[applicationId.ToLower()]) .FirstOrDefault(); @@ -252,7 +236,7 @@ public IApplicationContext GetApplcation(Type application) { if (application == null) return null; - var items = Dictionary.Values.SelectMany(x => x.Values) + var items = _dictionary.Values.SelectMany(x => x.Values) .Where(x => x.ApplicationClass.Equals(application)) .FirstOrDefault(); @@ -303,12 +287,12 @@ public IEnumerable GetApplcations(IEnumerable appli /// The contexts of the applications as an enumeration. public IEnumerable GetApplcations(IPluginContext pluginContext) { - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return new List(); } - return Dictionary[pluginContext].Values.Select(x => x.ApplicationContext); + return _dictionary[pluginContext].Values.Select(x => x.ApplicationContext); } /// @@ -322,11 +306,11 @@ public void Boot(IPluginContext pluginContext) return; } - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:applicationmanager.application.boot.notfound", pluginContext.PluginId @@ -336,27 +320,16 @@ public void Boot(IPluginContext pluginContext) return; } - foreach (var applicationItem in Dictionary[pluginContext]?.Values ?? Enumerable.Empty()) + foreach (var applicationItem in _dictionary[pluginContext]?.Values ?? Enumerable.Empty()) { var token = applicationItem.CancellationTokenSource.Token; - // Initialize application - applicationItem.Application.Initialization(applicationItem.ApplicationContext); - HttpServerContext.Log.Debug - ( - InternationalizationManager.I18N - ( - "webexpress:applicationmanager.application.initialization", - applicationItem.ApplicationContext.ApplicationId - ) - ); - // Run the application concurrently Task.Run(() => { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:applicationmanager.application.processing.start", applicationItem.ApplicationContext.ApplicationId) @@ -364,9 +337,9 @@ public void Boot(IPluginContext pluginContext) applicationItem.Application.Run(); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:applicationmanager.application.processing.end", applicationItem.ApplicationContext.ApplicationId @@ -384,7 +357,7 @@ public void Boot(IPluginContext pluginContext) /// The context of the plugin that contains the applications. public void ShutDown(IPluginContext pluginContext) { - foreach (var applicationItem in Dictionary[pluginContext]?.Values ?? Enumerable.Empty()) + foreach (var applicationItem in _dictionary[pluginContext]?.Values ?? Enumerable.Empty()) { applicationItem.CancellationTokenSource.Cancel(); } @@ -401,17 +374,17 @@ public void Remove(IPluginContext pluginContext) return; } - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return; } - foreach (var applicationContext in Dictionary[pluginContext]) + foreach (var applicationContext in _dictionary[pluginContext]) { OnRemoveApplication(applicationContext.Value.ApplicationContext); } - Dictionary.Remove(pluginContext); + _dictionary.Remove(pluginContext); } /// @@ -445,7 +418,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(deep) + - InternationalizationManager.I18N("webexpress:applicationmanager.application", applicationContext.ApplicationId) + I18N.Translate("webexpress:applicationmanager.application", applicationContext.ApplicationId) ); } } diff --git a/src/WebExpress.WebCore/WebApplication/IApplication.cs b/src/WebExpress.WebCore/WebApplication/IApplication.cs index 4c7ffd4..d4e56d6 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplication.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplication.cs @@ -1,18 +1,13 @@ using System; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebApplication { /// /// This interface represents an application. /// - public interface IApplication : IDisposable + public interface IApplication : IComponent, IDisposable { - /// - /// Initialization of the application. Here, for example, managed resources can be loaded. - /// - /// The application context. - void Initialization(IApplicationContext applicationContext); - /// /// Called when the application starts working. The call is concurrent. /// diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs index e4f50e9..02e4e67 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -7,7 +8,7 @@ namespace WebExpress.WebCore.WebApplication /// /// The application context. /// - public interface IApplicationContext + public interface IApplicationContext : IContext { /// /// Provides the context of the associated plugin. diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs new file mode 100644 index 0000000..b5dca09 --- /dev/null +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebApplication +{ + /// + /// Interface of the management of WebExpress applications. + /// + public interface IApplicationManager : IManager + { + /// + /// An event that fires when an application is added. + /// + event EventHandler AddApplication; + + /// + /// An event that fires when an application is removed. + /// + event EventHandler RemoveApplication; + + /// + /// Returns the stored applications. + /// + IEnumerable Applications { get; } + + /// + /// Determines the application contexts for a given application id. + /// + /// The application id. + /// The context of the application or null. + IApplicationContext GetApplcation(string applicationId); + + /// + /// Determines the application contexts for a given application id. + /// + /// The application type. + /// The context of the application or null. + IApplicationContext GetApplcation(Type application); + + /// + /// Determines the application contexts for the given application ids. + /// + /// The applications ids. Can contain regular expressions or * for all. + /// The contexts of the applications as an enumeration. + IEnumerable GetApplcations(IEnumerable applicationIds); + + /// + /// Determines the application contexts for the given plugin. + /// + /// The context of the plugin. + /// The contexts of the applications as an enumeration. + IEnumerable GetApplcations(IPluginContext pluginContext); + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs index ae4ecef..1cbd23a 100644 --- a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs @@ -3,7 +3,7 @@ /// /// The unique identification key. /// - public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute + public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IResourceAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs new file mode 100644 index 0000000..fbbbfbb --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -0,0 +1,91 @@ +using System; +using System.Linq; +using System.Reflection; + +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Provides methods to create instances of components with dependency injection. + /// + public static class ComponentActivator + { + /// + /// Creates an instance of the specified component type with the provided context and component manager. + /// + /// The type of the component, which must implement . + /// The type of the component to create. + /// The component manager to use for dependency injection. + /// An instance of the specified component type. + public static T CreateInstance(Type componentType, ComponentManager componentManager) where T : class, IManager + { + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentManager) ? componentManager : + parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentManager) ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return Activator.CreateInstance(componentType) as T; + } + + /// + /// Creates an instance of the specified component type with the provided context and component manager. + /// + /// The type of the component, which must implement . + /// The type of the context, which must implement . + /// The type of the component to create. + /// The context to pass to the component's constructor. + /// The component manager to use for dependency injection. + /// An instance of the specified component type. + public static T CreateInstance(Type componentType, C context, IComponentManager componentManager) where T : class, IComponent where C : IContext + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentManager) ? componentManager : + parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : + parameter.ParameterType == typeof(C) ? context : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentManager) ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return Activator.CreateInstance(componentType) as T; + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs index 38cc81c..f4307a6 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs @@ -17,7 +17,7 @@ public class ComponentItem /// /// Returns the component instance or null if not already created. /// - public IComponent ComponentInstance { get; internal set; } + public IManager ComponentInstance { get; internal set; } /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs index 8ae0327..7b24deb 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebEvent; @@ -21,17 +20,24 @@ namespace WebExpress.WebCore.WebComponent /// /// Central management of components. /// - public class ComponentManager + public class ComponentManager : IComponentManager { + private readonly InternationalizationManager _internationalizationManager; + private readonly PluginManager _pluginManager; + private readonly ApplicationManager _applicationManager; + private readonly ModuleManager _moduleManager; + private readonly ResourceManager _resourceManager; + private readonly SitemapManager _sitemapManager; + /// /// An event that fires when an component is added. /// - public event EventHandler AddComponent; + public event EventHandler AddComponent; /// /// An event that fires when an component is removed. /// - public event EventHandler RemoveComponent; + public event EventHandler RemoveComponent; /// /// Returns the reference to the context of the host. @@ -44,20 +50,20 @@ public class ComponentManager private ComponentDictionary Dictionary { get; } = []; /// - /// Returns all registered components. + /// Returns all registered managers. /// - public IEnumerable Components => new IComponent[] + public IEnumerable Managers => new IManager[] { LogManager, PackageManager, - PluginManager, + _pluginManager, ApplicationManager, - ModuleManager, + _moduleManager, EventManager, JobManager, StatusPageManager, - SitemapManager, - InternationalizationManager, + _sitemapManager, + _internationalizationManager, SessionManager, TaskManager }.Concat(Dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); @@ -78,19 +84,19 @@ public class ComponentManager /// Returns the plugin manager. /// /// The instance of the plugin manager or null. - public PluginManager PluginManager { get; private set; } + public IPluginManager PluginManager => _pluginManager; /// /// Returns the application manager. /// /// The instance of the application manager or null. - public ApplicationManager ApplicationManager { get; private set; } + public IApplicationManager ApplicationManager => _applicationManager; /// /// Returns the module manager. /// /// The instance of the module manager or null. - public ModuleManager ModuleManager { get; private set; } + public IModuleManager ModuleManager => _moduleManager; /// /// Returns the event manager. @@ -114,19 +120,19 @@ public class ComponentManager /// Returns the resource manager. /// /// The instance of the resource manager or null. - public ResourceManager ResourceManager { get; private set; } + public IResourceManager ResourceManager => _resourceManager; /// /// Returns the sitemap manager. /// /// The instance of the sitemap manager or null. - public SitemapManager SitemapManager { get; private set; } + public ISitemapManager SitemapManager => _sitemapManager; /// /// Returns the internationalization manager. /// /// The instance of the internationalization manager or null. - public InternationalizationManager InternationalizationManager { get; private set; } + public IInternationalizationManager InternationalizationManager => _internationalizationManager; /// /// Returns the session manager. @@ -148,29 +154,29 @@ internal ComponentManager(IHttpServerContext httpServerContext) { HttpServerContext = httpServerContext; - InternationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); - - HttpServerContext.Log.Debug - ( - InternationalizationManager.I18N("webexpress:componentmanager.initialization") - ); - // order is relevant LogManager = CreateInstance(typeof(LogManager)) as LogManager; PackageManager = CreateInstance(typeof(PackageManager)) as PackageManager; - PluginManager = CreateInstance(typeof(PluginManager)) as PluginManager; - InternationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; - ApplicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; - ModuleManager = CreateInstance(typeof(ModuleManager)) as ModuleManager; - ResourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; + _pluginManager = CreateInstance(typeof(PluginManager)) as PluginManager; + _internationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; + _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; + _moduleManager = CreateInstance(typeof(ModuleManager)) as ModuleManager; + _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; StatusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; - SitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; + _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; SessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; - PluginManager.AddPlugin += (sender, pluginContext) => + _internationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); + + HttpServerContext.Log.Debug + ( + _internationalizationManager.Translate("webexpress:componentmanager.initialization") + ); + + _pluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; @@ -186,20 +192,20 @@ internal ComponentManager(IHttpServerContext httpServerContext) /// /// The component class. /// The instance of the create and initialized component. - private IComponent CreateInstance(Type componentType) + private IManager CreateInstance(Type componentType) { if (componentType == null) { return null; } - else if (!componentType.GetInterfaces().Where(x => x == typeof(IComponent)).Any()) + else if (!componentType.GetInterfaces().Where(x => x == typeof(IManager)).Any()) { HttpServerContext.Log.Warning ( - InternationalizationManager.I18N + _internationalizationManager.Translate ( "webexpress:componentmanager.wrongtype", - componentType?.FullName, typeof(IComponent).FullName + componentType?.FullName, typeof(IManager).FullName ) ); @@ -208,35 +214,7 @@ private IComponent CreateInstance(Type componentType) try { - var flags = BindingFlags.NonPublic | BindingFlags.Instance; - var constructors = componentType?.GetConstructors(flags); - - if (constructors != null) - { - foreach (var constructor in constructors) - { - // injection - var parameters = constructor.GetParameters(); - var parameterValues = parameters.Select(parameter => - parameter.ParameterType == typeof(ComponentManager) ? this : - parameter.ParameterType == typeof(IHttpServerContext) ? HttpServerContext : - GetType().GetProperty - ( - parameter.Name, - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance - )?.GetValue(this) ?? null - ).ToArray(); - - var component = constructor.Invoke(parameterValues) as IComponent; - - if (component != null) - { - component.Initialization(HttpServerContext); - - return component; - } - } - } + return ComponentActivator.CreateInstance(componentType, this); } catch (Exception ex) { @@ -251,7 +229,7 @@ private IComponent CreateInstance(Type componentType) /// /// The id. /// The instance of the component or null. - public IComponent GetComponent(string id) + public IManager GetComponent(string id) { return Dictionary.Values .SelectMany(x => x) @@ -265,7 +243,7 @@ public IComponent GetComponent(string id) /// /// The component class. /// The instance of the component or null. - public T GetComponent() where T : IComponent + public T GetComponent() where T : IManager { return (T)Dictionary.Values .SelectMany(x => x) @@ -291,7 +269,7 @@ internal void Register(IPluginContext pluginContext) Dictionary.Add(pluginContext, []); var componentItems = Dictionary[pluginContext]; - foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IComponent).Name) != null)) + foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IManager).Name) != null)) { var id = type.FullName?.ToLower(); @@ -309,7 +287,7 @@ internal void Register(IPluginContext pluginContext) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:componentmanager.register", id) + _internationalizationManager.Translate("webexpress:componentmanager.register", id) ); // raises the AddComponent event @@ -319,7 +297,7 @@ internal void Register(IPluginContext pluginContext) { HttpServerContext.Log.Warning ( - InternationalizationManager.I18N("webexpress:componentmanager.duplicate", id) + _internationalizationManager.Translate("webexpress:componentmanager.duplicate", id) ); } } @@ -343,9 +321,9 @@ public void Register(IEnumerable pluginContexts) /// The plugin context. internal void BootComponent(IPluginContext pluginContext) { - PluginManager.Boot(pluginContext); - ApplicationManager.Boot(pluginContext); - ModuleManager.Boot(pluginContext); + _pluginManager.Boot(pluginContext); + _applicationManager.Boot(pluginContext); + _moduleManager.Boot(pluginContext); foreach (var component in Dictionary.Values .Where(x => x is IExecutableElements) @@ -374,7 +352,7 @@ internal void Execute() { HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:componentmanager.execute") + _internationalizationManager.Translate("webexpress:componentmanager.execute") ); PackageManager.Execute(); @@ -388,7 +366,7 @@ internal void ShutDown() { HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:componentmanager.shutdown") + _internationalizationManager.Translate("webexpress:componentmanager.shutdown") ); } @@ -398,9 +376,9 @@ internal void ShutDown() /// The plugin context. internal void ShutDownComponent(IPluginContext pluginContext) { - PluginManager.ShutDown(pluginContext); - ApplicationManager.ShutDown(pluginContext); - ModuleManager.ShutDown(pluginContext); + _pluginManager.ShutDown(pluginContext); + _applicationManager.ShutDown(pluginContext); + _moduleManager.ShutDown(pluginContext); foreach (var component in Dictionary.Values .Where(x => x is IExecutableElements) @@ -439,13 +417,13 @@ public void Remove(IPluginContext pluginContext) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:componentmanager.remove") + _internationalizationManager.Translate("webexpress:componentmanager.remove") ); } - ModuleManager.Remove(pluginContext); - ApplicationManager.Remove(pluginContext); - PluginManager.Remove(pluginContext); + _moduleManager.Remove(pluginContext); + _applicationManager.Remove(pluginContext); + _pluginManager.Remove(pluginContext); Dictionary.Remove(pluginContext); } @@ -454,7 +432,7 @@ public void Remove(IPluginContext pluginContext) /// Raises the AddComponent event. /// /// The component. - private void OnAddComponent(IComponent component) + private void OnAddComponent(IManager component) { AddComponent?.Invoke(null, component); } @@ -463,7 +441,7 @@ private void OnAddComponent(IComponent component) /// Raises the RemoveComponent event. /// /// The component. - private void OnRemoveComponent(IComponent component) + private void OnRemoveComponent(IManager component) { RemoveComponent?.Invoke(null, component); } @@ -476,7 +454,7 @@ internal void LogStatus() using var frame = new LogFrameSimple(HttpServerContext.Log); var output = new List { - InternationalizationManager.I18N("webexpress:componentmanager.component") + _internationalizationManager.Translate("webexpress:componentmanager.component") }; foreach (var pluginContext in PluginManager.Plugins) @@ -484,29 +462,29 @@ internal void LogStatus() output.Add ( string.Empty.PadRight(2) + - InternationalizationManager.I18N("webexpress:pluginmanager.plugin", pluginContext.PluginId) + _internationalizationManager.Translate("webexpress:pluginmanager.plugin", pluginContext.PluginId) ); - ApplicationManager.PrepareForLog(pluginContext, output, 4); - ModuleManager.PrepareForLog(pluginContext, output, 4); - ResourceManager.PrepareForLog(pluginContext, output, 4); + _applicationManager.PrepareForLog(pluginContext, output, 4); + _moduleManager.PrepareForLog(pluginContext, output, 4); + _resourceManager.PrepareForLog(pluginContext, output, 4); StatusPageManager.PrepareForLog(pluginContext, output, 4); JobManager.PrepareForLog(pluginContext, output, 4); } - foreach (var item in Dictionary) - { - foreach (var component in item.Value) - { - output.Add - ( - string.Empty.PadRight(2) + - InternationalizationManager.I18N("webexpress:pluginmanager.plugin", item.Key.PluginId) - ); - - component.ComponentInstance?.PrepareForLog(item.Key, output, 4); - } - } + //foreach (var item in Dictionary) + //{ + // foreach (var component in item.Value) + // { + // output.Add + // ( + // string.Empty.PadRight(2) + + // I18N.Translate("webexpress:pluginmanager.plugin", item.Key.PluginId) + // ); + + // component.ComponentInstance?.PrepareForLog(item.Key, output, 4); + // } + //} HttpServerContext.Log.Info(string.Join(Environment.NewLine, output)); } diff --git a/src/WebExpress.WebCore/WebComponent/IComponent.cs b/src/WebExpress.WebCore/WebComponent/IComponent.cs index fc6ffc4..7954ea8 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponent.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponent.cs @@ -1,30 +1,10 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebComponent { /// - /// Interface of the manager classes. + /// Interface of a component. /// public interface IComponent { - /// - /// Returns the reference to the context of the host. - /// - static IHttpServerContext HttpServerContext { get; } - - /// - /// Initialization - /// - /// The reference to the context of the host. - void Initialization(IHttpServerContext httpServerContext); - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - void PrepareForLog(IPluginContext pluginContext, IList output, int deep); } } diff --git a/src/WebExpress.WebCore/WebComponent/IComponentManager.cs b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs new file mode 100644 index 0000000..b161b36 --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebJob; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPackage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebStatusPage; +using WebExpress.WebCore.WebTask; + +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Interface of the central management of manager components. + /// + public interface IComponentManager : IManager + { + /// + /// An event that fires when an component is added. + /// + event EventHandler AddComponent; + + /// + /// An event that fires when an component is removed. + /// + event EventHandler RemoveComponent; + + /// + /// Returns the reference to the context of the host. + /// + IHttpServerContext HttpServerContext { get; } + + /// + /// Returns all registered components. + /// + IEnumerable Managers { get; } + + /// + /// Returns the log manager. + /// + /// The instance of the log manager or null. + LogManager LogManager { get; } + + /// + /// Returns the package manager. + /// + /// The instance of the package manager or null. + PackageManager PackageManager { get; } + + /// + /// Returns the plugin manager. + /// + /// The instance of the plugin manager or null. + public IPluginManager PluginManager { get; } + /// + /// Returns the application manager. + /// + /// The instance of the application manager or null. + IApplicationManager ApplicationManager { get; } + + /// + /// Returns the module manager. + /// + /// The instance of the module manager or null. + IModuleManager ModuleManager { get; } + + /// + /// Returns the event manager. + /// + /// The instance of the event manager or null. + EventManager EventManager { get; } + + /// + /// Returns the job manager. + /// + /// The instance of the job manager or null. + JobManager JobManager { get; } + + /// + /// Returns the status page manager. + /// + /// The instance of the status page manager or null. + StatusPageManager StatusPageManager { get; } + + /// + /// Returns the resource manager. + /// + /// The instance of the resource manager or null. + IResourceManager ResourceManager { get; } + + /// + /// Returns the sitemap manager. + /// + /// The instance of the sitemap manager or null. + ISitemapManager SitemapManager { get; } + + /// + /// Returns the internationalization manager. + /// + /// The instance of the internationalization manager or null. + IInternationalizationManager InternationalizationManager { get; } + + /// + /// Returns the session manager. + /// + /// The instance of the session manager or null. + SessionManager SessionManager { get; } + + /// + /// Returns the task manager. + /// + /// The instance of the task manager manager or null. + TaskManager TaskManager { get; } + + /// + /// Returns a component based on its id. + /// + /// The id. + /// The instance of the component or null. + IManager GetComponent(string id); + + /// + /// Returns a component based on its type. + /// + /// The component class. + /// The instance of the component or null. + T GetComponent() where T : IManager; + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IContext.cs b/src/WebExpress.WebCore/WebComponent/IContext.cs new file mode 100644 index 0000000..cb0ac42 --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IContext.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Represents the interface for all web contexts. + /// + public interface IContext + { + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IManager.cs b/src/WebExpress.WebCore/WebComponent/IManager.cs new file mode 100644 index 0000000..46e8e0d --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IManager.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Interface of the manager classes. + /// + public interface IManager + { + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IComponentPlugin.cs b/src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs similarity index 95% rename from src/WebExpress.WebCore/WebComponent/IComponentPlugin.cs rename to src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs index 46981bc..796f974 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentPlugin.cs +++ b/src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebComponent /// /// Interface of the manager classes. /// - public interface IComponentPlugin : IComponent + public interface IManagerPlugin : IManager { /// /// Discovers and registers entries from the specified plugin. diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 41827de..9867868 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -11,63 +11,46 @@ namespace WebExpress.WebCore.WebEvent /// /// The event manager. /// - public sealed class EventManager : IComponentPlugin, ISystemComponent + public sealed class EventManager : IManagerPlugin, ISystemComponent { - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the directory where the events are listed. - /// - private EventDictionary Dictionary { get; } = new EventDictionary(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly EventDictionary _dictionary = []; /// /// Initializes a new instance of the class. /// /// The component manager. - internal EventManager(ComponentManager componentManager) + /// The reference to the context of the host. + internal EventManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - ComponentManager.ModuleManager.AddModule += (sender, moduleContext) => + _componentManager.ModuleManager.AddModule += (sender, moduleContext) => { AssignToModule(moduleContext); }; - ComponentManager.ModuleManager.RemoveModule += (sender, moduleContext) => + _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => { DetachFromModule(moduleContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:eventmanager.initialization" ) @@ -112,7 +95,7 @@ public void Register(IEnumerable pluginContexts) /// The context of the module. private void AssignToModule(IModuleContext moduleContext) { - //foreach (var scheduleItem in Dictionary.Values.SelectMany(x => x)) + //foreach (var scheduleItem in _dictionary.Values.SelectMany(x => x)) //{ // if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) // { @@ -127,7 +110,7 @@ private void AssignToModule(IModuleContext moduleContext) /// The context of the module. private void DetachFromModule(IModuleContext moduleContext) { - //foreach (var scheduleItem in Dictionary.Values.SelectMany(x => x)) + //foreach (var scheduleItem in _dictionary.Values.SelectMany(x => x)) //{ // if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) // { @@ -143,17 +126,17 @@ private void DetachFromModule(IModuleContext moduleContext) public void Remove(IPluginContext pluginContext) { //// the plugin has not been registered in the manager - //if (!Dictionary.ContainsKey(pluginContext)) + //if (!_dictionary.ContainsKey(pluginContext)) //{ // return; //} - //foreach (var scheduleItem in Dictionary[pluginContext]) + //foreach (var scheduleItem in _dictionary[pluginContext]) //{ // scheduleItem.Dispose(); //} - //Dictionary.Remove(pluginContext); + //_dictionary.Remove(pluginContext); } /// @@ -169,7 +152,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in // output.Add // ( // string.Empty.PadRight(deep) + - // InternationalizationManager.I18N + // I18N.Translate // ( // "webexpress:eventmanager.job", // scheduleItem.JobId, diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 4927b30..41c32f6 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -27,133 +27,116 @@ namespace WebExpress.WebCore { + /// + /// The class provides a web server application for WebExpress. + /// public class WebEx { + private static ComponentManager _componentManager; + private HttpServer _httpServer; + /// /// Returns or sets the name of the web server. /// public string Name { get; set; } = "WebExpress"; - /// - /// The http(s) server. - /// - private HttpServer HttpServer { get; set; } - - /// - /// Returns the component manager. - /// - public static ComponentManager ComponentManager { get; private set; } - /// /// Returns the program version. /// public static string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(); /// - /// An event that fires when an component is added. - /// - public static event EventHandler AddComponent - { - add { ComponentManager.AddComponent += value; } - remove { ComponentManager.AddComponent -= value; } - } - - /// - /// An event that fires when an component is removed. + /// Returns the component manager. /// - public static event EventHandler RemoveComponent - { - add { ComponentManager.RemoveComponent += value; } - remove { ComponentManager.RemoveComponent -= value; } - } + public static IComponentManager ComponentManager => _componentManager; /// /// Returns the reference to the context of the host. /// - public static IHttpServerContext HttpServerContext => ComponentManager.HttpServerContext; + public static IHttpServerContext HttpServerContext => _componentManager?.HttpServerContext; /// /// Returns all registered components. /// - public static IEnumerable Components => ComponentManager.Components; + public static IEnumerable Components => ComponentManager?.Managers; /// /// Returns the log manager. /// /// The instance of the log manager or null. - public static LogManager LogManager => ComponentManager.LogManager; + public static LogManager LogManager => ComponentManager?.LogManager; /// /// Returns the package manager. /// /// The instance of the package manager or null. - public static PackageManager PackageManager => ComponentManager.PackageManager; + public static PackageManager PackageManager => ComponentManager?.PackageManager; /// /// Returns the plugin manager. /// /// The instance of the plugin manager or null. - public static PluginManager PluginManager => ComponentManager.PluginManager; + public static IPluginManager PluginManager => ComponentManager?.PluginManager; /// /// Returns the application manager. /// /// The instance of the application manager or null. - public static ApplicationManager ApplicationManager => ComponentManager.ApplicationManager; + public static IApplicationManager ApplicationManager => ComponentManager?.ApplicationManager; /// /// Returns the module manager. /// /// The instance of the module manager or null. - public static ModuleManager ModuleManager => ComponentManager.ModuleManager; + public static IModuleManager ModuleManager => ComponentManager?.ModuleManager; /// /// Returns the event manager. /// /// The instance of the event manager or null. - public static EventManager EventManager => ComponentManager.EventManager; + public static EventManager EventManager => ComponentManager?.EventManager; /// /// Returns the job manager. /// /// The instance of the job manager or null. - public static JobManager JobManager => ComponentManager.JobManager; + public static JobManager JobManager => ComponentManager?.JobManager; /// /// Returns the status page manager. /// /// The instance of the status page manager or null. - public static StatusPageManager StatusPageManager => ComponentManager.StatusPageManager; + public static StatusPageManager StatusPageManager => ComponentManager?.StatusPageManager; /// /// Returns the resource manager. /// /// The instance of the resource manager or null. - public static ResourceManager ResourceManager => ComponentManager.ResourceManager; + public static IResourceManager ResourceManager => ComponentManager?.ResourceManager; /// /// Returns the sitemap manager. /// /// The instance of the sitemap manager or null. - public static SitemapManager SitemapManager => ComponentManager.SitemapManager; + public static ISitemapManager SitemapManager => ComponentManager?.SitemapManager; /// /// Returns the internationalization manager. /// /// The instance of the internationalization manager or null. - public static InternationalizationManager InternationalizationManager => ComponentManager.InternationalizationManager; + public static IInternationalizationManager InternationalizationManager => ComponentManager?.InternationalizationManager; /// /// Returns the session manager. /// /// The instance of the session manager or null. - public static SessionManager SessionManager => ComponentManager.SessionManager; + public static SessionManager SessionManager => ComponentManager?.SessionManager; /// /// Returns the task manager. /// /// The instance of the task manager manager or null. - public static TaskManager TaskManager => ComponentManager.TaskManager; + public static TaskManager TaskManager => ComponentManager?.TaskManager; /// /// Entry point of application. @@ -250,7 +233,7 @@ public int Execution(string[] args) Initialization(ArgumentParser.Current.GetValidArguments(args), Path.Combine(Path.Combine(Environment.CurrentDirectory, "config"), argumentDict["config"])); // start the manager - ComponentManager.Execute(); + _componentManager.Execute(); // starting the web server Start(); @@ -326,36 +309,36 @@ private void Initialization(string args, string configFile) null ); - HttpServer = new HttpServer(context) + _httpServer = new HttpServer(context) { Config = config }; - ComponentManager = HttpServer.ComponentManager; + _componentManager = CreateComponentManager(_httpServer.HttpServerContext); // start logging - HttpServer.HttpServerContext.Log.Begin(config.Log); + _httpServer.HttpServerContext.Log.Begin(config.Log); // log program start - HttpServer.HttpServerContext.Log.Seperator('/'); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.startup")); - HttpServer.HttpServerContext.Log.Info(message: "".PadRight(80, '-')); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.version"), args: Version); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.arguments"), args: args); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.workingdirectory"), args: Environment.CurrentDirectory); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.packagebase"), args: config.PackageBase); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.assetbase"), args: config.AssetBase); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.database"), args: config.DataBase); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.configurationdirectory"), args: Path.GetDirectoryName(configFile)); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.configuration"), args: Path.GetFileName(configFile)); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.logdirectory"), args: Path.GetDirectoryName(HttpServer.HttpServerContext.Log.Filename)); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.log"), args: Path.GetFileName(HttpServer.HttpServerContext.Log.Filename)); + _httpServer.HttpServerContext.Log.Seperator('/'); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.startup")); + _httpServer.HttpServerContext.Log.Info(message: "".PadRight(80, '-')); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.version"), args: Version); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.arguments"), args: args); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.workingdirectory"), args: Environment.CurrentDirectory); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.packagebase"), args: config.PackageBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.assetbase"), args: config.AssetBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.database"), args: config.DataBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.configurationdirectory"), args: Path.GetDirectoryName(configFile)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.configuration"), args: Path.GetFileName(configFile)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.logdirectory"), args: Path.GetDirectoryName(_httpServer.HttpServerContext.Log.Filename)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.log"), args: Path.GetFileName(_httpServer.HttpServerContext.Log.Filename)); foreach (var v in config.Endpoints) { - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.uri"), args: v.Uri); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.uri"), args: v.Uri); } - HttpServer.HttpServerContext.Log.Seperator('='); + _httpServer.HttpServerContext.Log.Seperator('='); if (!Directory.Exists(config.PackageBase)) { @@ -380,7 +363,7 @@ private void Initialization(string args, string configFile) /// private void Start() { - HttpServer.Start(); + _httpServer.Start(); Thread.CurrentThread.Join(); } @@ -390,20 +373,20 @@ private void Start() /// private void Exit() { - HttpServer.Stop(); + _httpServer.Stop(); // end of program log - HttpServer.HttpServerContext.Log.Seperator('='); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.errors"), args: HttpServer.HttpServerContext.Log.ErrorCount); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.warnings"), args: HttpServer.HttpServerContext.Log.WarningCount); - HttpServer.HttpServerContext.Log.Info(message: InternationalizationManager.I18N("webexpress:app.done")); - HttpServer.HttpServerContext.Log.Seperator('/'); + _httpServer.HttpServerContext.Log.Seperator('='); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.errors"), args: _httpServer.HttpServerContext.Log.ErrorCount); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.warnings"), args: _httpServer.HttpServerContext.Log.WarningCount); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.done")); + _httpServer.HttpServerContext.Log.Seperator('/'); // Stop running - ComponentManager.ShutDown(); + _componentManager.ShutDown(); // stop logging - HttpServer.HttpServerContext.Log.Close(); + _httpServer.HttpServerContext.Log.Close(); } /// @@ -411,9 +394,9 @@ private void Exit() /// /// The id. /// The instance of the component or null. - public static IComponent GetComponent(string id) + public static IManager GetComponent(string id) { - return ComponentManager.GetComponent(id); + return _componentManager.GetComponent(id); } /// @@ -421,9 +404,19 @@ public static IComponent GetComponent(string id) /// /// The component class. /// The instance of the component or null. - public static T GetComponent() where T : IComponent + public static T GetComponent() where T : IManager + { + return _componentManager.GetComponent(); + } + + /// + /// Creates and returns a new instance of . + /// + /// The HTTP server context used to initialize the component manager. + /// A new instance of . + protected virtual ComponentManager CreateComponentManager(IHttpServerContext httpServerContext) { - return ComponentManager.GetComponent(); + return new ComponentManager(httpServerContext); } } } diff --git a/src/WebExpress.WebCore/WebJob/Cron.cs b/src/WebExpress.WebCore/WebJob/Cron.cs index 43312b8..ba62452 100644 --- a/src/WebExpress.WebCore/WebJob/Cron.cs +++ b/src/WebExpress.WebCore/WebJob/Cron.cs @@ -109,7 +109,7 @@ private IEnumerable Parse(string value, int minValue, int maxValue) } else { - Context.Log.Warning(message: InternationalizationManager.I18N("webexpress:schedulermanager.cron.range"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.range"), args: value); } } else if (range.Length == 1) @@ -122,17 +122,17 @@ private IEnumerable Parse(string value, int minValue, int maxValue) } else { - Context.Log.Warning(message: InternationalizationManager.I18N("webexpress:schedulermanager.cron.range"), args: result); + Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.range"), args: result); } } else { - Context.Log.Warning(message: InternationalizationManager.I18N("webexpress:schedulermanager.cron.parseerror"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.parseerror"), args: value); } } else { - Context.Log.Warning(message: InternationalizationManager.I18N("webexpress:schedulermanager.cron.parseerror"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.parseerror"), args: value); } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index f571b8c..58d03c8 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -12,80 +12,51 @@ namespace WebExpress.WebCore.WebJob { /// - /// Processing of cyclic jobs + /// Processing of cyclic jobs. /// - public sealed class JobManager : IComponentPlugin, ISystemComponent, IExecutableElements + public sealed class JobManager : IManagerPlugin, ISystemComponent, IExecutableElements { - /// - /// Thread termination. - /// - private CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); - - /// - /// The clock for determining the execution of the crons. - /// - private Clock Clock { get; } = new Clock(); - - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the directory where the static jobs are listed. - /// - private ScheduleDictionary StaticScheduleDictionary { get; } = new ScheduleDictionary(); - - /// - /// Returns the directory where the dynamic jobs are listed. - /// - private IEnumerable DynamicScheduleList { get; set; } = new List(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly ScheduleDictionary _staticScheduleDictionary = []; + private readonly List _dynamicScheduleList = []; + private readonly CancellationTokenSource _tokenSource = new(); + private readonly Clock _clock = new(); /// /// Initializes a new instance of the class. /// /// The component manager. - internal JobManager(ComponentManager componentManager) + /// The reference to the context of the host. + internal JobManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - ComponentManager.ModuleManager.AddModule += (sender, moduleContext) => + _componentManager.ModuleManager.AddModule += (sender, moduleContext) => { AssignToModule(moduleContext); }; - ComponentManager.ModuleManager.RemoveModule += (sender, moduleContext) => + _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => { DetachFromModule(moduleContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.initialization" ) @@ -137,9 +108,9 @@ public void Register(IPluginContext pluginContext) if (string.IsNullOrWhiteSpace(moduleId)) { // no module specified - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.moduleless", id ) @@ -147,12 +118,12 @@ public void Register(IPluginContext pluginContext) } // register the job - if (!StaticScheduleDictionary.ContainsKey(pluginContext)) + if (!_staticScheduleDictionary.ContainsKey(pluginContext)) { - StaticScheduleDictionary.Add(pluginContext, new List()); + _staticScheduleDictionary.Add(pluginContext, new List()); } - var dictItem = StaticScheduleDictionary[pluginContext]; + var dictItem = _staticScheduleDictionary[pluginContext]; dictItem.Add(new ScheduleStaticItem() { @@ -163,23 +134,23 @@ public void Register(IPluginContext pluginContext) moduleId = moduleId }); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.job.register", moduleId, id ) ); // assign the job to existing modules. - foreach (var moduleContext in ComponentManager.ModuleManager.GetModules(pluginContext, moduleId)) + foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) { if (moduleContext.PluginContext != pluginContext) { // job is not part of the module - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.wrongmodule", moduleContext.ModuleId, id @@ -228,7 +199,7 @@ public IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob Instance = jobInstance }; - DynamicScheduleList = DynamicScheduleList.Append(item); + _dynamicScheduleList.Append(item); return jobInstance; } @@ -257,7 +228,7 @@ public IJob Register(IModuleContext moduleContext, Cron cron) where T : IJob Instance = jobInstance }; - DynamicScheduleList = DynamicScheduleList.Append(item); + _dynamicScheduleList.Append(item); return jobInstance; } @@ -268,7 +239,7 @@ public IJob Register(IModuleContext moduleContext, Cron cron) where T : IJob /// The context of the module. private void AssignToModule(IModuleContext moduleContext) { - foreach (var scheduleItem in StaticScheduleDictionary.Values.SelectMany(x => x)) + foreach (var scheduleItem in _staticScheduleDictionary.Values.SelectMany(x => x)) { if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) { @@ -283,7 +254,7 @@ private void AssignToModule(IModuleContext moduleContext) /// The context of the module. private void DetachFromModule(IModuleContext moduleContext) { - foreach (var scheduleItem in StaticScheduleDictionary.Values.SelectMany(x => x)) + foreach (var scheduleItem in _staticScheduleDictionary.Values.SelectMany(x => x)) { if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) { @@ -299,12 +270,12 @@ private void DetachFromModule(IModuleContext moduleContext) /// An enumeration of the schedule item for the given plugin. internal IEnumerable GetScheduleItems(IPluginContext pluginContext) { - if (pluginContext == null || !StaticScheduleDictionary.ContainsKey(pluginContext)) + if (pluginContext == null || !_staticScheduleDictionary.ContainsKey(pluginContext)) { return Enumerable.Empty(); } - return StaticScheduleDictionary[pluginContext]; + return _staticScheduleDictionary[pluginContext]; } /// @@ -314,7 +285,7 @@ internal void Execute() { Task.Factory.StartNew(() => { - while (!TokenSource.IsCancellationRequested) + while (!_tokenSource.IsCancellationRequested) { Update(); @@ -322,7 +293,7 @@ internal void Execute() Thread.Sleep(secendsLeft * 1000); } - }, TokenSource.Token); + }, _tokenSource.Token); } /// @@ -330,17 +301,17 @@ internal void Execute() /// private void Update() { - foreach (var clock in Clock.Synchronize()) + foreach (var clock in _clock.Synchronize()) { - foreach (var scheduleItemValue in StaticScheduleDictionary.Values + foreach (var scheduleItemValue in _staticScheduleDictionary.Values .SelectMany(x => x) .SelectMany(x => x.Dictionary.Values)) { - if (scheduleItemValue.JobContext.Cron.Matching(Clock)) + if (scheduleItemValue.JobContext.Cron.Matching(_clock)) { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.job.process", scheduleItemValue.JobContext.JobId @@ -350,17 +321,17 @@ private void Update() Task.Factory.StartNew(() => { scheduleItemValue.Instance?.Process(); - }, TokenSource.Token); + }, _tokenSource.Token); } } - foreach (var scheduleItemValue in DynamicScheduleList) + foreach (var scheduleItemValue in _dynamicScheduleList) { - if (scheduleItemValue.JobContext.Cron.Matching(Clock)) + if (scheduleItemValue.JobContext.Cron.Matching(_clock)) { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.job.process", scheduleItemValue.JobContext.JobId @@ -370,7 +341,7 @@ private void Update() Task.Factory.StartNew(() => { scheduleItemValue.Instance?.Process(); - }, TokenSource.Token); + }, _tokenSource.Token); } } } @@ -381,7 +352,7 @@ private void Update() /// public void ShutDown() { - TokenSource.Cancel(); + _tokenSource.Cancel(); } /// @@ -396,17 +367,17 @@ public void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (!StaticScheduleDictionary.ContainsKey(pluginContext)) + if (!_staticScheduleDictionary.ContainsKey(pluginContext)) { return; } - foreach (var scheduleItem in StaticScheduleDictionary[pluginContext]) + foreach (var scheduleItem in _staticScheduleDictionary[pluginContext]) { scheduleItem.Dispose(); } - StaticScheduleDictionary.Remove(pluginContext); + _staticScheduleDictionary.Remove(pluginContext); } /// @@ -415,7 +386,7 @@ public void Remove(IPluginContext pluginContext) /// The job to remove. public void Remove(IJob job) { - DynamicScheduleList = DynamicScheduleList.Where(x => x != job); + _dynamicScheduleList.RemoveAll(x => x == job); } /// @@ -431,7 +402,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(deep) + - InternationalizationManager.I18N + I18N.Translate ( "webexpress:jobmanager.job", scheduleItem.JobId, diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 5b40533..34324c0 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebLog { - public class LogManager : IComponentPlugin, ISystemComponent + public class LogManager : IManagerPlugin, ISystemComponent { /// /// An event that fires when an log is added. @@ -46,7 +46,7 @@ public void Initialization(IHttpServerContext context) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:logmanager.initialization") + I18N.Translate("webexpress:logmanager.initialization") ); } @@ -106,7 +106,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(deep) + - InternationalizationManager.I18N("webexpress:logmanager.titel") + I18N.Translate("webexpress:logmanager.titel") ); } } diff --git a/src/WebExpress.WebCore/WebMessage/HttpContext.cs b/src/WebExpress.WebCore/WebMessage/HttpContext.cs index 8191610..e621a4c 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpContext.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Net; using System.Text; -using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebMessage { @@ -12,7 +11,7 @@ public class HttpContext /// /// The context of the web server. /// - public IHttpServerContext ServerContext { get; protected set; } + public IHttpServerContext HttpServerContext { get; protected set; } /// /// Returns or sets the id. @@ -61,9 +60,8 @@ internal HttpContext() /// Initializes a new instance of the class. /// /// Initial set of features. - /// The context of the Web server. - /// The component manager. - public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext serverContext, ComponentManager componentManager) + /// The context of the Web server. + public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext httpServerContext) { var connectionFeature = contextFeatures.Get(); var requestFeature = contextFeatures.Get(); @@ -78,7 +76,7 @@ public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext server Encoding = requestFeature.Headers.ContentEncoding.Any() ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; Uri = new Uri(baseUri, requestFeature.RawTarget); - Request = new Request(contextFeatures, header, componentManager); + Request = new Request(contextFeatures, header, httpServerContext); } } } diff --git a/src/WebExpress.WebCore/WebMessage/Parameter.cs b/src/WebExpress.WebCore/WebMessage/Parameter.cs index 575f4b3..17c7a75 100644 --- a/src/WebExpress.WebCore/WebMessage/Parameter.cs +++ b/src/WebExpress.WebCore/WebMessage/Parameter.cs @@ -4,6 +4,9 @@ namespace WebExpress.WebCore.WebMessage { + /// + /// Represents a parameter with a key, value, and scope. + /// public class Parameter { /// @@ -12,12 +15,12 @@ public class Parameter public ParameterScope Scope { get; private set; } /// - /// The key. + /// Returns the key of the parameter. /// public string Key { get; private set; } /// - /// The value. + /// Returns the value of the parameter. /// public string Value { get; internal set; } diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index ec5f6e4..5bf43ed 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -8,7 +8,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebSession; using WebExpress.WebCore.WebUri; @@ -24,7 +23,7 @@ public class Request /// /// The context of the web server. /// - public IHttpServerContext ServerContext { get; protected set; } + public IHttpServerContext HttpServerContext { get; protected set; } /// /// Returns the request method (e.g. POST). @@ -41,11 +40,6 @@ public class Request /// private ParameterDictionary Param { get; } = []; - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Returns the session. /// @@ -123,7 +117,7 @@ public CultureInfo Culture } catch { - return ServerContext.Culture ?? CultureInfo.CurrentCulture; + return HttpServerContext.Culture ?? CultureInfo.CurrentCulture; } } } @@ -138,18 +132,17 @@ public CultureInfo Culture /// /// Initial set of features. /// The header. - /// The component manager. - internal Request(IFeatureCollection contextFeatures, RequestHeaderFields header, ComponentManager componentManager) + /// The context of the web server. + internal Request(IFeatureCollection contextFeatures, RequestHeaderFields header, IHttpServerContext httpServerContext) { var connectionFeature = contextFeatures.Get(); var requestFeature = contextFeatures.Get(); var requestIdentifierFeature = contextFeatures.Get(); //var sessionFeature = contextFeatures.Get(); - ServerContext = componentManager.HttpServerContext; + HttpServerContext = httpServerContext; RequestTraceIdentifier = requestIdentifierFeature.TraceIdentifier; Protocoll = requestFeature.Protocol; - ComponentManager = componentManager; Scheme = requestFeature.Scheme.ToLower() switch { @@ -456,7 +449,7 @@ private void ParseRequestParams() /// private void ParseSessionParams() { - Session = ComponentManager.SessionManager?.GetSession(this); + Session = WebEx.ComponentManager.SessionManager?.GetSession(this); var property = Session?.GetProperty(); if (property != null && property.Params != null) diff --git a/src/WebExpress.WebCore/WebModule/IModule.cs b/src/WebExpress.WebCore/WebModule/IModule.cs index e68e9c0..8639e39 100644 --- a/src/WebExpress.WebCore/WebModule/IModule.cs +++ b/src/WebExpress.WebCore/WebModule/IModule.cs @@ -1,15 +1,13 @@ using System; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebModule { - public interface IModule : IDisposable + /// + /// Interface of a module + /// + public interface IModule : IComponent, IDisposable { - /// - /// Instillation of the module. Here, for example, managed resources can be loaded. - /// - /// The module context. - void Initialization(IModuleContext moduleContext); - /// /// Called when the module starts working. The call is concurrent. /// diff --git a/src/WebExpress.WebCore/WebModule/IModuleManager.cs b/src/WebExpress.WebCore/WebModule/IModuleManager.cs new file mode 100644 index 0000000..45bf179 --- /dev/null +++ b/src/WebExpress.WebCore/WebModule/IModuleManager.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebModule +{ + /// + /// The interface of the module manager. + /// + public interface IModuleManager : IManager + { + /// + /// An event that fires when an module is added. + /// + event EventHandler AddModule; + + /// + /// An event that fires when an module is removed. + /// + event EventHandler RemoveModule; + + /// + /// Returns all stored modules. + /// + public IEnumerable Modules { get; } + + /// + /// Determines the module for a given application context and module id. + /// + /// The type of the application. + /// The type of the module. + /// The context of the module or null. + public IModuleContext GetModule(Type applicationType, Type moduleType); + + /// + /// Determines the module for a given application context and module id. + /// + /// The context of the application. + /// The modul id. + /// The context of the module or null. + public IModuleContext GetModule(IApplicationContext applicationContext, string moduleId); + + /// + /// Determines the module for a given application context and module id. + /// + /// The context of the application. + /// The module class. + /// The context of the module or null. + public IModuleContext GetModule(IApplicationContext applicationContext, Type moduleClass); + + /// + /// Determines the module for a given plugin context and module id. + /// + /// The context of the plugin. + /// The modul id. + /// An enumeration of the module contexts for the given plugin and module id. + public IEnumerable GetModules(IPluginContext pluginContext, string moduleId); + + /// + /// Determines the module for a given plugin context and module id. + /// + /// The context of the plugin. + /// The modul id. + /// An enumeration of the module contexts for the given plugin and module id. + public IEnumerable GetModules(IApplicationContext applicationContext); + } +} diff --git a/src/WebExpress.WebCore/WebModule/ModuleContext.cs b/src/WebExpress.WebCore/WebModule/ModuleContext.cs index f595b0f..ff9b4d7 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleContext.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleContext.cs @@ -64,7 +64,7 @@ internal ModuleContext() /// The string that uniquely represents the module. public override string ToString() { - return $"Module {ModuleId}"; + return $"{ApplicationContext?.ApplicationId}:{ModuleId}"; } } } diff --git a/src/WebExpress.WebCore/WebModule/ModuleItem.cs b/src/WebExpress.WebCore/WebModule/ModuleItem.cs index c161fe6..792d07d 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleItem.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleItem.cs @@ -172,25 +172,12 @@ public void Boot() // thread termination. var token = item.CancellationTokenSource.Token; - // initialize module - item.ModuleInstance.Initialization(item.ModuleContext); - - Log.Debug - ( - message: InternationalizationManager.I18N - ( - "webexpress:modulemanager.module.initialization", - item.ModuleContext.ApplicationContext.ApplicationId, - item.ModuleContext.PluginContext.PluginId - ) - ); - // execute modules concurrently Task.Run(() => { Log.Debug ( - message: InternationalizationManager.I18N + message: I18N.Translate ( "webexpress:modulemanager.module.processing.start", item.ModuleContext.ApplicationContext.ApplicationId, @@ -202,7 +189,7 @@ public void Boot() Log.Debug ( - message: InternationalizationManager.I18N + message: I18N.Translate ( "webexpress:modulemanager.module.processing.end", item.ModuleContext.ApplicationContext.ApplicationId, diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index 248ccfd..5607dc4 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -13,8 +13,12 @@ namespace WebExpress.WebCore.WebModule /// /// The module manager manages the WebExpress modules. /// - public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISystemComponent + public sealed class ModuleManager : IModuleManager, IManagerPlugin, IExecutableElements, ISystemComponent { + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly ModuleDictionary _dictionary = []; + /// /// An event that fires when an module is added. /// @@ -25,25 +29,10 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst /// public event EventHandler RemoveModule; - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the directory where the modules are listed. - /// - private ModuleDictionary Dictionary { get; } = new ModuleDictionary(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Delivers all stored modules. /// - public IEnumerable Modules => Dictionary.Values + public IEnumerable Modules => _dictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x.Dictionary.Values) .Select(x => x.ModuleContext); @@ -52,42 +41,36 @@ public sealed class ModuleManager : IComponentPlugin, IExecutableElements, ISyst /// Initializes a new instance of the class. /// /// The component manager. - internal ModuleManager(ComponentManager componentManager) + /// The reference to the context of the host. + private ModuleManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - ComponentManager.ApplicationManager.AddApplication += (sender, applicationContext) => + _componentManager.ApplicationManager.AddApplication += (sender, applicationContext) => { AssignToApplication(applicationContext); }; - ComponentManager.ApplicationManager.RemoveApplication += (sender, applicationContext) => + _componentManager.ApplicationManager.RemoveApplication += (sender, applicationContext) => { DetachFromApplication(applicationContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:modulemanager.initialization") + I18N.Translate("webexpress:modulemanager.initialization") ); } @@ -97,7 +80,7 @@ public void Initialization(IHttpServerContext context) /// A context of a plugin whose modules are to be registered. public void Register(IPluginContext pluginContext) { - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.ContainsKey(pluginContext)) { return; } @@ -113,7 +96,7 @@ public void Register(IPluginContext pluginContext) )) { var id = type.FullName?.ToLower(); - var name = type.Name; + var name = type.Name.ToLower(); var icon = string.Empty; var description = string.Empty; var contextPath = string.Empty; @@ -162,14 +145,14 @@ public void Register(IPluginContext pluginContext) if (!applicationIds.Any()) { // no application specified - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N("webexpress:modulemanager.applicationless", id) + I18N.Translate("webexpress:modulemanager.applicationless", id) ); } - Dictionary.TryAdd(pluginContext, new Dictionary()); - var item = Dictionary[pluginContext]; + _dictionary.TryAdd(pluginContext, new Dictionary()); + var item = _dictionary[pluginContext]; if (!item.ContainsKey(id)) { @@ -186,7 +169,7 @@ public void Register(IPluginContext pluginContext) AssetPath = assetPath, ContextPath = new UriResource(contextPath), DataPath = dataPath, - Log = HttpServerContext.Log + Log = _httpServerContext.Log }; moduleItem.AddModule += (s, e) => @@ -202,14 +185,14 @@ public void Register(IPluginContext pluginContext) item.Add(id, moduleItem); // assign the module to existing applications. - foreach (var applicationContext in ComponentManager.ApplicationManager.Applications) + foreach (var applicationContext in _componentManager.ApplicationManager.Applications) { AssignToApplication(applicationContext); } - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:modulemanager.register", id, @@ -219,9 +202,9 @@ public void Register(IPluginContext pluginContext) } else { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:modulemanager.duplicate", id, @@ -250,7 +233,7 @@ public void Register(IEnumerable pluginContexts) /// The context of the application. private void AssignToApplication(IApplicationContext applicationContext) { - foreach (var moduleItem in Dictionary.Values.SelectMany(x => x.Values)) + foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) { if (moduleItem.Applications.Contains("*") || moduleItem.Applications.Contains(applicationContext?.ApplicationId?.ToLower())) @@ -266,7 +249,7 @@ private void AssignToApplication(IApplicationContext applicationContext) /// The context of the application. private void DetachFromApplication(IApplicationContext applicationContext) { - foreach (var moduleItem in Dictionary.Values.SelectMany(x => x.Values)) + foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) { if (moduleItem.Applications.Contains("*") || moduleItem.Applications.Contains(applicationContext?.ApplicationId?.ToLower())) @@ -284,8 +267,8 @@ private void DetachFromApplication(IApplicationContext applicationContext) /// The context of the module or null. public IModuleContext GetModule(Type applicationType, Type moduleType) { - var applicationContext = ComponentManager.ApplicationManager.GetApplcation(applicationType); - var item = Dictionary.Values + var applicationContext = _componentManager.ApplicationManager.GetApplcation(applicationType); + var item = _dictionary.Values .SelectMany(x => x.Values) .Where(x => x.Dictionary.ContainsKey(applicationContext)) .Where(x => x.ModuleClass.Equals(moduleType)) @@ -304,7 +287,7 @@ public IModuleContext GetModule(Type applicationType, Type moduleType) /// The context of the module or null. public IModuleContext GetModule(IApplicationContext applicationContext, string moduleId) { - var item = Dictionary.Values + var item = _dictionary.Values .SelectMany(x => x.Values) .Where(x => x.Dictionary.ContainsKey(applicationContext)) .Select(x => x.Dictionary[applicationContext]) @@ -348,7 +331,7 @@ public IEnumerable GetModules(IPluginContext pluginContext, stri /// An enumeration of the module contexts for the given plugin and module id. public IEnumerable GetModules(IApplicationContext applicationContext) { - return Dictionary.Values + return _dictionary.Values .SelectMany(x => x.Values) .Where(x => x.Dictionary.ContainsKey(applicationContext)) .Select(x => x.Dictionary[applicationContext]) @@ -362,12 +345,12 @@ public IEnumerable GetModules(IApplicationContext applicationCon /// An enumeration of the module contexts for the given plugin. internal IEnumerable GetModuleItems(IPluginContext pluginContext) { - if (pluginContext == null || !Dictionary.ContainsKey(pluginContext)) + if (pluginContext == null || !_dictionary.ContainsKey(pluginContext)) { - return Enumerable.Empty(); + return []; } - return Dictionary[pluginContext].Values; + return _dictionary[pluginContext].Values; } /// @@ -402,7 +385,7 @@ public void ShutDown(IPluginContext pluginContext) /// The context of the application containing the modules. public void ShutDown(IApplicationContext applicationContext) { - foreach (var moduleItem in Dictionary.Values.SelectMany(x => x.Values)) + foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) { // terminate module moduleItem.ShutDown(applicationContext); @@ -420,19 +403,19 @@ public void Remove(IPluginContext pluginContext) return; } - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return; } ShutDown(pluginContext); - foreach (var moduleItem in Dictionary[pluginContext].Values) + foreach (var moduleItem in _dictionary[pluginContext].Values) { moduleItem.Dispose(); } - Dictionary.Remove(pluginContext); + _dictionary.Remove(pluginContext); } /// @@ -466,7 +449,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(deep) + - InternationalizationManager.I18N + I18N.Translate ( "webexpress:modulemanager.module", moduleContext.ModuleId, diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index afef9f6..b6e3133 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -19,8 +19,11 @@ namespace WebExpress.WebCore.WebPackage /// /// The package manager manages packages with WebExpress extensions. The packages must be in WebExpressPackage format (*.wxp). /// - public sealed class PackageManager : IComponent, ISystemComponent + public sealed class PackageManager : IManager, ISystemComponent { + private readonly ComponentManager _componentManager; + private readonly PluginManager _pluginManager; + /// /// An event that fires when an package is added. /// @@ -46,31 +49,22 @@ public sealed class PackageManager : IComponent, ISystemComponent /// private PackageCatalog Catalog { get; } = new PackageCatalog(); - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Initializes a new instance of the class. /// /// The component manager. - internal PackageManager(ComponentManager componentManager) - { - ComponentManager = componentManager; - } - - /// - /// Initialization - /// + /// The plugin manager. /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) + private PackageManager(IComponentManager componentManager, IPluginManager pluginManager, IHttpServerContext context) { + _componentManager = componentManager as ComponentManager; + _pluginManager = pluginManager as PluginManager; + HttpServerContext = context; HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:packagemanager.initialization") + I18N.Translate("webexpress:packagemanager.initialization") ); } @@ -80,10 +74,10 @@ public void Initialization(IHttpServerContext context) internal void Execute() { // load the default plugins - ComponentManager.PluginManager.Register(); + _pluginManager.Register(); // boot default elements - ComponentManager.BootComponent(ComponentManager.PluginManager.Plugins); + _componentManager.BootComponent(_pluginManager.Plugins); LoadCatalog(); @@ -95,7 +89,7 @@ internal void Execute() HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:packagemanager.existing", package.File) + I18N.Translate("webexpress:packagemanager.existing", package.File) ); if (package.State != PackageCatalogeItemState.Disable) @@ -110,7 +104,7 @@ internal void Execute() SaveCatalog(); // build sitemap - ComponentManager.SitemapManager.Refresh(); + _componentManager.SitemapManager.Refresh(); Task.Factory.StartNew(() => { @@ -140,7 +134,7 @@ public void Scan() { HttpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:packagemanager.scan", HttpServerContext.PackagePath @@ -171,7 +165,7 @@ public void Scan() HttpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:packagemanager.add", package @@ -187,7 +181,7 @@ public void Scan() HttpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:packagemanager.remove", package @@ -229,7 +223,7 @@ public void Scan() if (newPackages.Any() || removePackages.Any()) { // build sitemap - ComponentManager.SitemapManager.Refresh(); + _componentManager.SitemapManager.Refresh(); // save the catalog SaveCatalog(); @@ -303,7 +297,7 @@ private PackageCatalogItem LoadPackage(string file) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:packagemanager.packagenotfound", file @@ -347,7 +341,7 @@ private void SaveCatalog() HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:packagemanager.save") + I18N.Translate("webexpress:packagemanager.save") ); } @@ -409,12 +403,12 @@ private void RegisterPackage(PackageCatalogItem package) // load plugins foreach (var plugin in package?.Metadata.PluginSources ?? Enumerable.Empty()) { - var pluginContexts = ComponentManager.PluginManager.Register(GetTargetPath(package, plugin)); + var pluginContexts = _pluginManager.Register(GetTargetPath(package, plugin)); package.Plugins.AddRange(pluginContexts); } - ComponentManager.LogStatus(); + _componentManager.LogStatus(); } /// @@ -423,7 +417,7 @@ private void RegisterPackage(PackageCatalogItem package) /// The package. private void BootPackage(PackageCatalogItem package) { - ComponentManager.BootComponent(package.Plugins); + _componentManager.BootComponent(package.Plugins); } /// diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index af1026e..8eb72d9 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -110,7 +110,7 @@ public virtual void AddHeaderScriptLinks(string url) public virtual IHtmlNode Render(RenderContext context) { var html = new HtmlElementRootHtml(); - html.Head.Title = InternationalizationManager.I18N(context.Request, context.Page?.Title); + html.Head.Title = I18N.Translate(context.Request, context.Page?.Title); html.Head.Favicons = Favicons?.Select(x => new Favicon(x.Url, x.Mediatype)); //html.Head.Base = Context.ContextPath.ToString(); html.Head.Styles = Styles; diff --git a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs index c89b617..2e364f4 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs @@ -1,18 +1,13 @@ using System; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPlugin { /// /// This interface represents a plugin. /// - public interface IPlugin : IDisposable + public interface IPlugin : IComponent, IDisposable { - /// - /// Initialization of the plugin. Here, for example, managed resources can be loaded. - /// - /// The plugin context. - void Initialization(IPluginContext pluginContext); - /// /// Called when the plugin starts working. The call is concurrent. /// diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs b/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs index 034df6a..a114344 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs @@ -1,4 +1,5 @@ using System.Reflection; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPlugin @@ -6,7 +7,7 @@ namespace WebExpress.WebCore.WebPlugin /// /// The context of a plugin. /// - public interface IPluginContext + public interface IPluginContext : IContext { /// /// The assembly that contains the plugin. diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs new file mode 100644 index 0000000..9ecc646 --- /dev/null +++ b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebPlugin +{ + /// + /// The plugin manager manages the WebExpress plugins. + /// + public interface IPluginManager : IManager + { + /// + /// An event that fires when an plugin is added. + /// + event EventHandler AddPlugin; + + /// + /// An event that fires when an plugin is removed. + /// + event EventHandler RemovePlugin; + + /// + /// Returns all plugins. + /// + IEnumerable Plugins { get; } + + /// + /// Returns a plugin context based on its id. + /// + /// The id of the plugin. + /// The plugin context. + IPluginContext GetPlugin(string pluginId); + + /// + /// Returns a plugin context based on its id. + /// + /// The type of the plugin. + /// The plugin context. + IPluginContext GetPlugin(Type plugin); + } +} diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index c598767..ca878b3 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -15,8 +15,13 @@ namespace WebExpress.WebCore.WebPlugin /// /// The plugin manager manages the WebExpress plugins. /// - public sealed class PluginManager : IComponent, IExecutableElements, ISystemComponent + public sealed class PluginManager : IPluginManager, IExecutableElements, ISystemComponent { + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly PluginDictionary _dictionary = []; + private readonly PluginDictionary _unfulfilledDependencies = []; + /// /// An event that fires when an plugin is added. /// @@ -27,61 +32,35 @@ public sealed class PluginManager : IComponent, IExecutableElements, ISystemComp /// public event EventHandler RemovePlugin; - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - - /// - /// Returns the directory where the plugins are listed. - /// - private PluginDictionary Dictionary { get; } = []; - - /// - /// Plugins that do not meet the dependencies. - /// - private PluginDictionary UnfulfilledDependencies { get; } = []; - /// /// Returns all plugins. /// - public IEnumerable Plugins => Dictionary.Values.Select(x => x.PluginContext).ToList(); + public IEnumerable Plugins => _dictionary.Values.Select(x => x.PluginContext).ToList(); /// /// Initializes a new instance of the class. /// /// The component manager. - internal PluginManager(ComponentManager componentManager) + /// The reference to the context of the host. + private PluginManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.AddComponent += (s, e) => + _componentManager.AddComponent += (s, e) => { //AssignToComponent(e); }; - ComponentManager.RemoveComponent += (s, e) => + _componentManager.RemoveComponent += (s, e) => { //DetachFromcomponent(e); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:pluginmanager.initialization") + I18N.Translate("webexpress:pluginmanager.initialization") ); } @@ -103,9 +82,9 @@ internal void Register() if (assembly != null) { assemblies.Add(assembly); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.load", assembly.GetName().Name, @@ -154,9 +133,9 @@ internal IEnumerable Register(string pluginFile) if (assembly != null) { assemblies.Add(assembly); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.load", assembly.GetName().Name, @@ -209,11 +188,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex foreach (var customAttribute in type.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPluginAttribute)))) { - if (customAttribute.AttributeType == typeof(IdAttribute)) - { - id = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()?.ToLower() ?? id; - } - else if (customAttribute.AttributeType == typeof(NameAttribute)) + if (customAttribute.AttributeType == typeof(NameAttribute)) { name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } @@ -238,39 +213,39 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginName = name, Manufacturer = type.Assembly.GetCustomAttribute()?.Company, Copyright = type.Assembly.GetCustomAttribute()?.Copyright, - Icon = UriResource.Combine(HttpServerContext?.ContextPath, icon), + Icon = UriResource.Combine(_httpServerContext?.ContextPath, icon), Description = description, Version = type.Assembly.GetCustomAttribute()?.InformationalVersion, - Host = HttpServerContext + Host = _httpServerContext }; hasUnfulfilledDependencies = HasUnfulfilledDependencies(id, dependencies); if (hasUnfulfilledDependencies) { - UnfulfilledDependencies.Add(id, new PluginItem() + _unfulfilledDependencies.Add(id, new PluginItem() { PluginLoadContext = loadContext, PluginClass = type, PluginContext = pluginContext, - Plugin = Activator.CreateInstance(type) as IPlugin, + Plugin = ComponentActivator.CreateInstance(type, pluginContext, _componentManager), Dependencies = dependencies }); } - else if (!Dictionary.ContainsKey(id)) + else if (!_dictionary.ContainsKey(id)) { - Dictionary.Add(id, new PluginItem() + _dictionary.Add(id, new PluginItem() { PluginLoadContext = loadContext, PluginClass = type, PluginContext = pluginContext, - Plugin = Activator.CreateInstance(type) as IPlugin, + Plugin = ComponentActivator.CreateInstance(type, pluginContext, _componentManager), Dependencies = dependencies }); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:pluginmanager.created", id) + I18N.Translate("webexpress:pluginmanager.created", id) ); OnAddPlugin(pluginContext); @@ -279,9 +254,9 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex } else { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N("webexpress:pluginmanager.duplicate", id) + I18N.Translate("webexpress:pluginmanager.duplicate", id) ); } @@ -291,16 +266,16 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex } else { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N("webexpress:pluginmanager.tomany", type.FullName) + I18N.Translate("webexpress:pluginmanager.tomany", type.FullName) ); } } } catch (Exception ex) { - HttpServerContext.Log.Exception(ex); + _httpServerContext.Log.Exception(ex); } return plugins; @@ -322,7 +297,7 @@ public void Remove(IPluginContext pluginContext) var pluginItem = GetPluginItem(pluginContext); pluginItem?.PluginLoadContext?.Unload(); - Dictionary.Remove(pluginContext.PluginId); + _dictionary.Remove(pluginContext.PluginId); } /// @@ -336,7 +311,7 @@ private void CheckUnfulfilledDependencies() { fulfilledDependencies = false; - foreach (var unfulfilledDependencies in UnfulfilledDependencies) + foreach (var unfulfilledDependencies in _unfulfilledDependencies) { var hasUnfulfilledDependencies = HasUnfulfilledDependencies ( @@ -347,14 +322,14 @@ private void CheckUnfulfilledDependencies() if (!hasUnfulfilledDependencies) { fulfilledDependencies = true; - UnfulfilledDependencies.Remove(unfulfilledDependencies.Key); - Dictionary.Add(unfulfilledDependencies.Key, unfulfilledDependencies.Value); + _unfulfilledDependencies.Remove(unfulfilledDependencies.Key); + _dictionary.Add(unfulfilledDependencies.Key, unfulfilledDependencies.Value); OnAddPlugin(unfulfilledDependencies.Value.PluginContext); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.fulfilleddependencies", unfulfilledDependencies.Key @@ -376,14 +351,14 @@ private bool HasUnfulfilledDependencies(string id, IEnumerable dependenc var hasUnfulfilledDependencies = false; foreach (var dependency in dependencies - .Where(x => !Dictionary.ContainsKey(x.ToLower()))) + .Where(x => !_dictionary.ContainsKey(x.ToLower()))) { // dependency was not fulfilled hasUnfulfilledDependencies = true; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.unfulfilleddependencies", id, @@ -402,7 +377,7 @@ private bool HasUnfulfilledDependencies(string id, IEnumerable dependenc /// The plugin context. public IPluginContext GetPlugin(string pluginId) { - return Dictionary.Values + return _dictionary.Values .Where ( x => x.PluginContext != null && @@ -419,7 +394,7 @@ public IPluginContext GetPlugin(string pluginId) /// The plugin context. public IPluginContext GetPlugin(Type plugin) { - return Dictionary.Values + return _dictionary.Values .Where ( x => x.PluginContext != null && @@ -438,11 +413,11 @@ private PluginItem GetPluginItem(IPluginContext pluginContext) { var pluginId = pluginContext?.PluginId?.ToLower(); - if (pluginId == null || !Dictionary.TryGetValue(pluginId, out PluginItem value)) + if (pluginId == null || !_dictionary.TryGetValue(pluginId, out PluginItem value)) { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.notavailable", pluginId @@ -469,23 +444,23 @@ internal void Boot(IPluginContext pluginContext) return; } - // initialize plugin - pluginItem.Plugin.Initialization(pluginItem.PluginContext); - HttpServerContext.Log.Debug - ( - InternationalizationManager.I18N - ( - "webexpress:pluginmanager.plugin.initialization", - pluginItem.PluginContext.PluginId - ) - ); + //// initialize plugin + //pluginItem.Plugin.Initialization(pluginItem.PluginContext); + //HttpServerContext.Log.Debug + //( + // I18N.Translate + // ( + // "webexpress:pluginmanager.plugin.initialization", + // pluginItem.PluginContext.PluginId + // ) + //); // run plugin concurrently Task.Run(() => { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.plugin.processing.start", pluginItem.PluginContext.PluginId @@ -494,9 +469,9 @@ internal void Boot(IPluginContext pluginContext) pluginItem.Plugin.Run(); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.plugin.processing.end", pluginItem.PluginContext.PluginId @@ -566,44 +541,44 @@ private void OnRemovePlugin(IPluginContext pluginContext) /// private void Logging() { - using var frame = new LogFrameSimple(HttpServerContext.Log); + using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List(); - HttpServerContext.Log.Info + _httpServerContext.Log.Info ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:pluginmanager.pluginmanager.label" ) ); - list.AddRange(Dictionary + list.AddRange(_dictionary .Where ( x => x.Value.PluginClass.Assembly .GetCustomAttribute(typeof(SystemPluginAttribute)) != null ) - .Select(x => InternationalizationManager.I18N + .Select(x => I18N.Translate ( "webexpress:pluginmanager.pluginmanager.system", x.Key )) ); - list.AddRange(Dictionary + list.AddRange(_dictionary .Where ( x => x.Value.PluginClass.Assembly .GetCustomAttribute(typeof(SystemPluginAttribute)) == null ) - .Select(x => InternationalizationManager.I18N + .Select(x => I18N.Translate ( "webexpress:pluginmanager.pluginmanager.custom", x.Key )) ); - list.AddRange(UnfulfilledDependencies - .Select(x => InternationalizationManager.I18N + list.AddRange(_unfulfilledDependencies + .Select(x => I18N.Translate ( "webexpress:pluginmanager.pluginmanager.unfulfilleddependencies", x.Key @@ -612,7 +587,7 @@ private void Logging() foreach (var item in list) { - HttpServerContext.Log.Info(string.Join(Environment.NewLine, item)); + _httpServerContext.Log.Info(string.Join(Environment.NewLine, item)); } } diff --git a/src/WebExpress.WebCore/WebResource/IResource.cs b/src/WebExpress.WebCore/WebResource/IResource.cs index fb973b3..747a92d 100644 --- a/src/WebExpress.WebCore/WebResource/IResource.cs +++ b/src/WebExpress.WebCore/WebResource/IResource.cs @@ -1,16 +1,10 @@ -using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebResource { - public interface IResource : II18N + public interface IResource : IComponent { - /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. - /// - /// The context of the resource. - void Initialization(IResourceContext resourceContext); - /// /// Preprocessing of the resource. /// diff --git a/src/WebExpress.WebCore/WebResource/IResourceContext.cs b/src/WebExpress.WebCore/WebResource/IResourceContext.cs index e0f7654..dc97be1 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -6,18 +7,13 @@ namespace WebExpress.WebCore.WebResource { - public interface IResourceContext + public interface IResourceContext : IContext { /// /// Returns the associated plugin context. /// IPluginContext PluginContext { get; } - ///// - ///// Returns the associated application context. - ///// - //IApplicationContext ApplicationContext { get; } - /// /// Returns the corresponding module context. /// @@ -55,6 +51,11 @@ public interface IResourceContext /// bool Cache { get; } + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + bool IncludeSubPaths { get; } + /// /// Returns the context path. /// diff --git a/src/WebExpress.WebCore/WebResource/IResourceManager.cs b/src/WebExpress.WebCore/WebResource/IResourceManager.cs new file mode 100644 index 0000000..ca2a8a8 --- /dev/null +++ b/src/WebExpress.WebCore/WebResource/IResourceManager.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebResource +{ + /// + /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). + /// + public interface IResourceManager : IManager + { + /// + /// An event that fires when an resource is added. + /// + event EventHandler AddResource; + + /// + /// An event that fires when an resource is removed. + /// + event EventHandler RemoveResource; + + /// + /// Returns all resource contexts. + /// + IEnumerable Resources { get; } + + /// + /// Returns an enumeration of all containing resource contexts of a plugin. + /// + /// A context of a plugin whose resources are to be registered. + /// An enumeration of resource contexts. + IEnumerable GetResorces(IPluginContext pluginContext); + + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// An enumeration of resource contextes. + IEnumerable GetResorces() where T : IResource; + + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// An enumeration of resource contextes. + IEnumerable GetResorces(Type resourceType); + + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// The context of the module. + /// An enumeration of resource contextes. + IEnumerable GetResorces(IModuleContext moduleContext) where T : IResource; + + /// + /// Returns the resource context. + /// + /// The context of the module. + /// The resource id. + /// An resource context or null. + IResourceContext GetResorce(IModuleContext moduleContext, string resourceId); + + /// + /// Returns the resource context. + /// + /// The context of the module. + /// The resource type. + /// An resource context or null. + IResourceContext GetResorce(IModuleContext moduleContext, Type resourceType); + + /// + /// Returns the resource context. + /// + /// The application id. + /// The module id. + /// The resource id. + /// An resource context or null. + IResourceContext GetResorce(string applicationId, string moduleId, string resourceId); + + /// + /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. + /// + /// The context used for resource creation. + /// The culture with the language settings. + /// The created or cached resource. + IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture); + } +} diff --git a/src/WebExpress.WebCore/WebResource/Resource.cs b/src/WebExpress.WebCore/WebResource/Resource.cs index 8c7a2cc..863f95a 100644 --- a/src/WebExpress.WebCore/WebResource/Resource.cs +++ b/src/WebExpress.WebCore/WebResource/Resource.cs @@ -7,11 +7,6 @@ namespace WebExpress.WebCore.WebResource { public abstract class Resource : IResource { - ///// - ///// Returns the resource id. - ///// - //public string Id { get; internal set; } - /// /// Returns the context of the application. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index cfdb1a0..d03b3b0 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -126,7 +126,7 @@ public override Response Process(Request request) break; } - request.ServerContext.Log.Debug(InternationalizationManager.I18N + request.HttpServerContext.Log.Debug(I18N.Translate ( "webexpress:resource.file", request.RemoteEndPoint, request.Uri diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index ecdf401..0edf477 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -9,18 +8,19 @@ namespace WebExpress.WebCore.WebResource { + /// + /// Represents the context of a resource. + /// public class ResourceContext : IResourceContext { + private readonly IResourceManager _resourceManager; + private readonly ResourceItem _resourceItem; + /// /// Returns the associated plugin context. /// public IPluginContext PluginContext { get; private set; } - /// - /// Returns the associated application context. - /// - public IApplicationContext ApplicationContext => ModuleContext?.ApplicationContext; - /// /// Returns the corresponding module context. /// @@ -51,9 +51,9 @@ public class ResourceContext : IResourceContext /// /// Returns the parent or null if not used. /// - public IResourceContext ParentContext => WebEx.ComponentManager.ResourceManager.Resources - .Where(x => !string.IsNullOrWhiteSpace(ResourceItem.ParentId)) - .Where(x => x.ResourceId.Equals(ResourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) + public IResourceContext ParentContext => _resourceManager.Resources + .Where(x => !string.IsNullOrWhiteSpace(_resourceItem.ParentId)) + .Where(x => x.ResourceId.Equals(_resourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) .FirstOrDefault(); @@ -62,6 +62,11 @@ public class ResourceContext : IResourceContext /// public bool Cache { get; internal set; } + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; internal set; } + /// /// Returns the context path. /// @@ -72,31 +77,39 @@ public UriResource ContextPath var parentContext = ParentContext; if (parentContext != null) { - return UriResource.Combine(ParentContext?.Uri, ResourceItem.ContextPath); + return UriResource.Combine(ParentContext?.Uri, _resourceItem.ContextPath); } - return UriResource.Combine(ModuleContext.ContextPath, ResourceItem.ContextPath); + return UriResource.Combine(ModuleContext.ContextPath, _resourceItem.ContextPath); } } /// /// Returns the uri. /// - public UriResource Uri => ContextPath.Append(ResourceItem.PathSegment); + public UriResource Uri => ContextPath.Append(_resourceItem.PathSegment); /// /// Initializes a new instance of the class. /// /// The module context. - internal ResourceContext(IModuleContext moduleContext) + /// The resource manager. + /// The resource item or null. + internal ResourceContext(IModuleContext moduleContext, IResourceManager resourceManager, ResourceItem resourceItem = null) { PluginContext = moduleContext?.PluginContext; ModuleContext = moduleContext; + _resourceManager = resourceManager; + _resourceItem = resourceItem; } /// - /// Returns or sets the resource item. + /// Returns a string that represents the current object. /// - internal ResourceItem ResourceItem { get; set; } + /// A string that represents the current object. + public override string ToString() + { + return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{ResourceId}"; + } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceFile.cs b/src/WebExpress.WebCore/WebResource/ResourceFile.cs index 1553090..371f23d 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceFile.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceFile.cs @@ -117,7 +117,7 @@ public override Response Process(Request request) break; } - request.ServerContext.Log.Debug(InternationalizationManager.I18N("webexpress:resource.file", request.RemoteEndPoint, request.Uri)); + request.HttpServerContext.Log.Debug(I18N.Translate("webexpress:resource.file", request.RemoteEndPoint, request.Uri)); return response; } diff --git a/src/WebExpress.WebCore/WebResource/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/ResourceItem.cs index d83b41f..44f04e6 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceItem.cs @@ -13,8 +13,10 @@ namespace WebExpress.WebCore.WebResource /// /// A resource element that contains meta information about a resource. /// - public class ResourceItem : IDisposable + internal class ResourceItem : IDisposable { + private readonly IResourceManager _resourceManager; + /// /// An event that fires when an ressource is added. /// @@ -113,6 +115,15 @@ public class ResourceItem : IDisposable /// public IEnumerable ResourceContexts => Dictionary.Values; + /// + /// Initializes a new instance of the class. + /// + /// The resource manager. + internal ResourceItem(IResourceManager resourceManager) + { + _resourceManager = resourceManager; + } + /// /// Adds an module assignment /// @@ -122,20 +133,20 @@ public void AddModule(IModuleContext moduleContext) // only if no instance has been created yet if (Dictionary.ContainsKey(moduleContext)) { - Log.Warning(message: InternationalizationManager.I18N("webexpress:resourcemanager.addresource.duplicate", ResourceId, moduleContext.ModuleId)); + Log.Warning(message: I18N.Translate("webexpress:resourcemanager.addresource.duplicate", ResourceId, moduleContext.ModuleId)); return; } // create context - var resourceContext = new ResourceContext(moduleContext) + var resourceContext = new ResourceContext(moduleContext, _resourceManager, this) { Scopes = Scopes, Conditions = Conditions, ResourceId = ResourceId, ResourceTitle = Title, Cache = Cache, - ResourceItem = this + IncludeSubPaths = IncludeSubPaths }; if diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index b1a957b..155ae0f 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebAttribute; @@ -16,8 +17,12 @@ namespace WebExpress.WebCore.WebResource /// /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). /// - public sealed class ResourceManager : IComponentPlugin, ISystemComponent + public sealed class ResourceManager : IResourceManager, IManagerPlugin, ISystemComponent { + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly ResourceDictionary _dictionary = []; + /// /// An event that fires when an resource is added. /// @@ -28,30 +33,15 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent /// public event EventHandler RemoveResource; - /// - /// Returns the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - - /// - /// Returns the directory where the resources are listed. - /// - private ResourceDictionary Dictionary { get; } = new ResourceDictionary(); - /// /// Returns all resource items. /// - internal IEnumerable ResourceItems => Dictionary.Values.SelectMany(x => x.Values); + internal IEnumerable ResourceItems => _dictionary.Values.SelectMany(x => x.Values); /// /// Returns all resource contexts. /// - public IEnumerable Resources => Dictionary.Values + public IEnumerable Resources => _dictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x.ResourceContexts); @@ -59,42 +49,36 @@ public sealed class ResourceManager : IComponentPlugin, ISystemComponent /// Initializes a new instance of the class. /// /// The component manager. - internal ResourceManager(ComponentManager componentManager) + /// The reference to the context of the host. + internal ResourceManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - ComponentManager.ModuleManager.AddModule += (sender, moduleContext) => + _componentManager.ModuleManager.AddModule += (sender, moduleContext) => { AssignToModule(moduleContext); }; - ComponentManager.ModuleManager.RemoveModule += (sender, moduleContext) => + _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => { DetachFromModule(moduleContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:resourcemanager.initialization") + I18N.Translate("webexpress:resourcemanager.initialization") ); } @@ -104,15 +88,15 @@ public void Initialization(IHttpServerContext context) /// A context of a plugin whose resources are to be registered. public void Register(IPluginContext pluginContext) { - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.ContainsKey(pluginContext)) { return; } var assembly = pluginContext?.Assembly; - Dictionary.Add(pluginContext, new Dictionary()); - var dict = Dictionary[pluginContext]; + _dictionary.Add(pluginContext, new Dictionary()); + var dict = _dictionary[pluginContext]; foreach (var resourceType in assembly.GetTypes() .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) @@ -187,9 +171,9 @@ public void Register(IPluginContext pluginContext) if (string.IsNullOrEmpty(moduleId)) { // no module specified - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:resourcemanager.moduleless", id @@ -201,7 +185,7 @@ public void Register(IPluginContext pluginContext) if (!dict.ContainsKey(id)) { - var resourceItem = new ResourceItem() + var resourceItem = new ResourceItem(_componentManager.ResourceManager) { ResourceId = id, Title = title, @@ -215,7 +199,7 @@ public void Register(IPluginContext pluginContext) IncludeSubPaths = includeSubPaths, PathSegment = segment.ToPathSegment(), Optional = optional, - Log = HttpServerContext.Log + Log = _httpServerContext?.Log }; resourceItem.AddResource += (s, e) => @@ -230,9 +214,9 @@ public void Register(IPluginContext pluginContext) dict.Add(id, resourceItem); - HttpServerContext.Log.Debug + _httpServerContext?.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:resourcemanager.addresource", id, @@ -242,7 +226,7 @@ public void Register(IPluginContext pluginContext) } // assign the resource to existing modules. - foreach (var moduleContext in ComponentManager.ModuleManager.GetModules(pluginContext, moduleId)) + foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) { AssignToModule(moduleContext); } @@ -267,7 +251,7 @@ public void Register(IEnumerable pluginContexts) /// The context of the module. private void AssignToModule(IModuleContext moduleContext) { - foreach (var resourceItem in Dictionary.Values + foreach (var resourceItem in _dictionary.Values .SelectMany(x => x.Values) .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) .Where(x => !x.IsAssociatedWithModule(moduleContext))) @@ -282,7 +266,7 @@ private void AssignToModule(IModuleContext moduleContext) /// The context of the module. private void DetachFromModule(IModuleContext moduleContext) { - foreach (var resourceItem in Dictionary.Values + foreach (var resourceItem in _dictionary.Values .SelectMany(x => x.Values) .Where(x => !x.IsAssociatedWithModule(moduleContext))) { @@ -291,46 +275,124 @@ private void DetachFromModule(IModuleContext moduleContext) } /// - /// Renturns an enumeration of all containing resource items of a plugin. + /// Returns an enumeration of all containing resource items of a plugin. /// /// A context of a plugin whose resources are to be registered. /// An enumeration of resource items. internal IEnumerable GetResorceItems(IPluginContext pluginContext) { - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { - return new List(); + return []; } - return Dictionary[pluginContext].Values; + return _dictionary[pluginContext].Values; } /// - /// Renturns an enumeration of all containing resource contexts of a plugin. + /// Returns an enumeration of all containing resource contexts of a plugin. /// /// A context of a plugin whose resources are to be registered. /// An enumeration of resource contexts. public IEnumerable GetResorces(IPluginContext pluginContext) { - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return new List(); } - return Dictionary[pluginContext].Values + return _dictionary[pluginContext].Values .SelectMany(x => x.ResourceContexts); } /// - /// Renturns the resource context. + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// An enumeration of resource contextes. + public IEnumerable GetResorces() where T : IResource + { + return GetResorces(typeof(T)); + } + + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// An enumeration of resource contextes. + public IEnumerable GetResorces(Type resourceType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .Where(x => x.ResourceClass.Equals(resourceType)) + .Select(x => x.ResourceContext); + } + + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// The context of the module. + /// An enumeration of resource contextes. + public IEnumerable GetResorces(IModuleContext moduleContext) where T : IResource + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceClass.Equals(typeof(T))) + .Select(x => x.ResourceContext); + } + + /// + /// Returns the resource context. + /// + /// The context of the module. + /// The resource type. + /// An resource context or null. + public IResourceContext GetResorce(IModuleContext moduleContext, Type resourceType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .Where(x => x.ResourceContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.ResourceContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceClass.Equals(resourceType)) + .Select(x => x.ResourceContext) + .FirstOrDefault(); + } + + /// + /// Returns the resource context. + /// + /// The context of the module. + /// The resource id. + /// An resource context or null. + public IResourceContext GetResorce(IModuleContext moduleContext, string resourceId) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .Where(x => x.ResourceContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.ResourceContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceContext.ResourceId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.ResourceContext) + .FirstOrDefault(); + } + + /// + /// Returns the resource context. /// /// The application id. /// The module id. /// The resource id. /// An resource context or null. - public IResourceContext GetResorces(string applicationId, string moduleId, string resourceId) + public IResourceContext GetResorce(string applicationId, string moduleId, string resourceId) { - return Dictionary.Values + return _dictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x.ResourceContexts) .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) @@ -340,6 +402,38 @@ public IResourceContext GetResorces(string applicationId, string moduleId, strin .FirstOrDefault(); } + /// + /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. + /// + /// The context used for resource creation. + /// The culture with the language settings. + /// The created or cached resource. + public IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture) + { + var resourceItem = _dictionary.Values + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.ResourceContexts.Contains(resourceContext)); + + if (resourceItem != null && resourceItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _componentManager); + + if (instance is II18N i18n) + { + i18n.Culture = culture; + } + + if (resourceItem.Cache) + { + resourceItem.Instance = instance; + } + + return instance; + } + + return resourceItem?.Instance; + } + /// /// Removes all resources associated with the specified plugin context. /// @@ -352,17 +446,17 @@ public void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return; } - foreach (var resourceItem in Dictionary[pluginContext].Values) + foreach (var resourceItem in _dictionary[pluginContext].Values) { resourceItem.Dispose(); } - Dictionary.Remove(pluginContext); + _dictionary.Remove(pluginContext); } /// @@ -396,7 +490,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(deep) + - InternationalizationManager.I18N + I18N.Translate ( "webexpress:resourcemanager.resource", resourcenItem.ResourceId, diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index c121598..1f78d4e 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebSession { - public class SessionManager : IComponent, ISystemComponent + public class SessionManager : IManager, ISystemComponent { /// /// Returns or sets the reference to the context of the host. @@ -37,7 +37,7 @@ public void Initialization(IHttpServerContext context) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:sessionmanager.initialization") + I18N.Translate("webexpress:sessionmanager.initialization") ); } diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs new file mode 100644 index 0000000..e05737b --- /dev/null +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebSitemap +{ + /// + /// The interface of the sitemap manager. + /// + public interface ISitemapManager : IManager + { + /// + /// Returns the side map. + /// + IEnumerable SiteMap { get; } + + /// + /// Rebuilds the sitemap. + /// + void Refresh(); + + /// + /// Locates the resource associated with the Uri. + /// + /// The Uri. + /// The search context. + /// The search result with the found resource or null + SearchResult SearchResource(Uri requestUri, SearchContext searchContext); + + /// + /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// + /// Returns the uri taking into account the context or null. + UriResource GetUri(params Parameter[] parameters) where T : IResource; + + /// + /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// + /// The resource type. + /// The parameters to be considered for the URI. + /// Returns the URI taking into account the context, or null if no valid URI is found. + UriResource GetUri(Type resourceType, params Parameter[] parameters); + + /// + /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The module context. + /// Returns the uri taking into account the context or null. + UriResource GetUri(IModuleContext moduleContext) where T : IResource; + + /// + /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The module context. + /// Returns the uri taking into account the context or null. + UriResource GetUri(IResourceContext resourceContext) where T : IResource; + } +} diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index 8d321ea..1824339 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -1,6 +1,4 @@ using System.Collections.Generic; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; @@ -14,12 +12,7 @@ public class SearchResult /// /// Returns the resource id. /// - public string Id { get; internal set; } - - /// - /// Returns the resource title. - /// - public string Title { get; internal set; } + public string ResourceId { get; internal set; } /// /// Returns the instance. @@ -31,16 +24,6 @@ public class SearchResult /// public SearchContext SearchContext { get; internal set; } - /// - /// Returns the context of the application. - /// - public IApplicationContext ApplicationContext { get; internal set; } - - /// - /// Returns the context of the module. - /// - public IModuleContext ModuleContext { get; internal set; } - /// /// Returns the context of the resource. /// diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 0562357..2b4bbe3 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -7,7 +7,6 @@ using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; @@ -15,50 +14,38 @@ namespace WebExpress.WebCore.WebSitemap { /// - /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). + /// The sitemap manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). /// - public sealed class SitemapManager : IComponent, ISystemComponent + public sealed class SitemapManager : ISitemapManager, ISystemComponent { - /// - /// Returns the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the side map. - /// - private SitemapNode Root { get; set; } = new SitemapNode(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } + private SitemapNode _root = new(); + private readonly IComponentManager _componentManager; + private readonly IResourceManager _resourceManager; + private readonly IHttpServerContext _httpServerContext; /// /// Returns the side map. /// - public IEnumerable SiteMap => Root.GetPreOrder().Select(x => x.ResourceContext); + public IEnumerable SiteMap => _root.GetPreOrder() + .Where(x => x != null) + .Select(x => x.ResourceContext); /// /// Initializes a new instance of the class. /// /// The component manager. - internal SitemapManager(ComponentManager componentManager) + /// The resource manager. + /// The reference to the context of the host. + private SitemapManager(IComponentManager componentManager, IResourceManager resourceManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; - } + _componentManager = componentManager; + _resourceManager = resourceManager as ResourceManager; - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:sitemapmanager.initialization") + I18N.Translate("webexpress:sitemapmanager.initialization") ); } @@ -69,19 +56,19 @@ public void Refresh() { var newSiteMapNode = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:sitemapmanager.refresh") + I18N.Translate("webexpress:sitemapmanager.refresh") ); // applications - var applications = ComponentManager.ApplicationManager.Applications + var applications = _componentManager.ApplicationManager.Applications .Select(x => new { ApplicationContext = x, x.ContextPath.PathSegments }) - .OrderBy(x => x.PathSegments.Count()); + .OrderBy(x => x.PathSegments.Count); foreach (var application in applications) { @@ -93,13 +80,13 @@ public void Refresh() } // modules - var modules = ComponentManager.ModuleManager.Modules + var modules = _componentManager.ModuleManager.Modules .Select(x => new { ModuleContext = x, x.ContextPath.PathSegments }) - .OrderBy(x => x.PathSegments.Count()); + .OrderBy(x => x.PathSegments.Count); foreach (var module in modules) { @@ -111,34 +98,29 @@ public void Refresh() } // resourcen - var resources = ComponentManager.ResourceManager.ResourceItems - .SelectMany(x => x.ResourceContexts - .Select(y => new + var resources = _resourceManager.Resources + .Select(x => new { - Item = x, - ResourceContext = y, - y.Uri.PathSegments - })) - .OrderBy(x => x.PathSegments.Count()); + ResourceContext = x, + x.Uri.PathSegments + }) + .OrderBy(x => x.PathSegments.Count); foreach (var item in resources) { MergeSitemap(newSiteMapNode, CreateSiteMap ( new Queue(item.PathSegments), - item.Item, item.ResourceContext )); } - Root = newSiteMapNode; + _root = newSiteMapNode; - using (var frame = new LogFrameSimple(HttpServerContext.Log)) - { - var list = new List(); - PrepareForLog(null, list, 2); - HttpServerContext.Log.Info(string.Join(Environment.NewLine, list)); - } + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List(); + PrepareForLog(null, list, 2); + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// @@ -152,8 +134,8 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) var variables = new Dictionary(); var result = SearchNode ( - Root, - new Queue(requestUri.Segments.Select(x => (x == "/" ? x : (x.EndsWith("/") ? x[..^1] : x)))), + _root, + new Queue(requestUri.Segments.Select(x => x == "/" ? x : (x.EndsWith("/") ? x[..^1] : x))), new Queue(), searchContext ); @@ -178,8 +160,27 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) /// Returns the uri taking into account the context or null. public UriResource GetUri(params Parameter[] parameters) where T : IResource { - var node = Root.GetPreOrder() - .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) + var resourceContexts = _resourceManager.GetResorces(); + + var node = _root.GetPreOrder() + .Where(x => resourceContexts.Contains(x.ResourceContext)) + .FirstOrDefault(); + + return node?.ResourceContext?.Uri.SetParameters(parameters); + } + + /// + /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// + /// The resource type. + /// The parameters to be considered for the URI. + /// Returns the URI taking into account the context, or null if no valid URI is found. + public UriResource GetUri(Type resourceType, params Parameter[] parameters) + { + var resourceContexts = _resourceManager.GetResorces(resourceType); + + var node = _root.GetPreOrder() + .Where(x => resourceContexts.Contains(x.ResourceContext)) .FirstOrDefault(); return node?.ResourceContext?.Uri.SetParameters(parameters); @@ -193,9 +194,10 @@ public UriResource GetUri(params Parameter[] parameters) where T : IResource /// Returns the uri taking into account the context or null. public UriResource GetUri(IModuleContext moduleContext) where T : IResource { - var node = Root.GetPreOrder() - .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) - .Where(x => x.ResourceContext.ModuleContext == moduleContext) + var resourceContexts = _resourceManager.GetResorces(moduleContext); + + var node = _root.GetPreOrder() + .Where(x => resourceContexts.Contains(x.ResourceContext)) .FirstOrDefault(); return node?.ResourceContext?.Uri; @@ -209,14 +211,42 @@ public UriResource GetUri(IModuleContext moduleContext) where T : IResource /// Returns the uri taking into account the context or null. public UriResource GetUri(IResourceContext resourceContext) where T : IResource { - var node = Root.GetPreOrder() - .Where(x => x.ResourceItem?.ResourceClass == typeof(T)) - .Where(x => x.ResourceContext.ModuleContext == resourceContext.ModuleContext) + var resourceContexts = _resourceManager.GetResorces(resourceContext.ModuleContext) + .Where(x => x.ResourceId.Equals(resourceContext.ResourceId, StringComparison.OrdinalIgnoreCase)); + + var node = _root.GetPreOrder() + .Where(x => resourceContexts.Contains(x.ResourceContext)) .FirstOrDefault(); return node?.ResourceContext?.Uri; } + /// + /// Creates the sitemap. Works recursively. + /// It is important for the algorithm that the addition of application is sorted + /// by the number of path segments in ascending order. + /// + /// The path segments of the context path. + /// The application context. + /// The parent node or null if root. + /// The sitemap root node. + private static SitemapNode CreateSiteMap + ( + Queue contextPathSegments, + IApplicationContext applicationContext + ) + { + var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; + var next = CreateSiteMap(contextPathSegments, applicationContext, root); + + if (next != null) + { + root.Children.Add(next); + } + + return root; + } + /// /// Creates the sitemap. Works recursively. /// It is important for the algorithm that the addition of application is sorted @@ -230,15 +260,20 @@ private static SitemapNode CreateSiteMap ( Queue contextPathSegments, IApplicationContext applicationContext, - SitemapNode parent = null + SitemapNode parent ) { var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + + if (pathSegment == null) + { + return null; + } + var node = new SitemapNode() { - PathSegment = pathSegment as IUriPathSegment, + PathSegment = pathSegment, Parent = parent, - //ApplicationContext = applicationContext }; if (contextPathSegments.Any()) @@ -249,6 +284,31 @@ private static SitemapNode CreateSiteMap return node; } + /// + /// Creates the sitemap. Works recursively. + /// It is important for the algorithm that the addition of module is sorted + /// by the number of path segments in ascending order. + /// + /// The path segments of the context path. + /// The module context. + /// The sitemap root node. + private static SitemapNode CreateSiteMap + ( + Queue contextPathSegments, + IModuleContext moduleContext + ) + { + var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; + var next = CreateSiteMap(contextPathSegments, moduleContext, root); + + if (next != null) + { + root.Children.Add(next); + } + + return root; + } + /// /// Creates the sitemap. Works recursively. /// It is important for the algorithm that the addition of module is sorted @@ -262,16 +322,20 @@ private static SitemapNode CreateSiteMap ( Queue contextPathSegments, IModuleContext moduleContext, - SitemapNode parent = null + SitemapNode parent ) { var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + + if (pathSegment == null) + { + return null; + } + var node = new SitemapNode() { PathSegment = pathSegment as IUriPathSegment, Parent = parent, - //ApplicationContext = moduleContext?.ApplicationContext, - //ModuleContext = moduleContext }; if (contextPathSegments.Any()) @@ -282,38 +346,64 @@ private static SitemapNode CreateSiteMap return node; } + /// + /// Creates the sitemap. Works recursively. + /// It is important for the algorithm that the addition of module is sorted + /// by the number of path segments in ascending order. + /// + /// The path segments of the context path. + /// The resource context. + /// The sitemap root node. + private static SitemapNode CreateSiteMap + ( + Queue contextPathSegments, + IResourceContext resourceContext + ) + { + var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; + var next = CreateSiteMap(contextPathSegments, resourceContext, root); + + if (next != null) + { + root.Children.Add(next); + } + + return root; + } + /// /// Creates the sitemap. Works recursively. /// It is important for the algorithm that the addition of resources is sorted /// by the number of path segments in ascending order. /// /// The path segments of the context path. - /// The resource item. /// The resource context. /// The parent node or null if root. /// The sitemap parent node. private static SitemapNode CreateSiteMap ( Queue contextPathSegments, - ResourceItem resourceItem, IResourceContext resourceContext, SitemapNode parent = null ) { var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + + if (pathSegment == null) + { + return null; + } + var node = new SitemapNode() { - PathSegment = pathSegment as IUriPathSegment, + PathSegment = pathSegment, Parent = parent, - ResourceItem = !contextPathSegments.Any() ? resourceItem : null, - //ApplicationContext = resourceContext?.ModuleContext?.ApplicationContext, - //ModuleContext = resourceContext?.ModuleContext, ResourceContext = resourceContext }; if (contextPathSegments.Any()) { - node.Children.Add(CreateSiteMap(contextPathSegments, resourceItem, resourceContext, node)); + node.Children.Add(CreateSiteMap(contextPathSegments, resourceContext, node)); } return node; @@ -324,7 +414,7 @@ private static SitemapNode CreateSiteMap /// /// The first sitemap to be merged. /// The second sitemap to be merged. - private void MergeSitemap(SitemapNode first, SitemapNode second) + private static void MergeSitemap(SitemapNode first, SitemapNode second) { if (first.PathSegment.Equals(second.PathSegment)) { @@ -332,14 +422,10 @@ private void MergeSitemap(SitemapNode first, SitemapNode second) { foreach (var fc in first.Children.Where(x => x.PathSegment.Equals(sc.PathSegment))) { - if (fc.ResourceItem == null) + if (fc.ResourceContext == null) { - fc.ResourceItem = sc.ResourceItem; - //fc.ApplicationContext = sc.ApplicationContext; - //fc.ModuleContext = sc.ModuleContext; fc.ResourceContext = sc.ResourceContext; - fc.Instance = sc.Instance; - fc.Parent = sc.Parent; + //fc.Parent = sc.Parent; } MergeSitemap(fc, sc); @@ -382,28 +468,22 @@ SearchContext searchContext outPathSegments.Enqueue(copy); - if (nextPathSegment == null && node.ResourceItem != null) + if (nextPathSegment == null) { return new SearchResult() { - Id = node.ResourceItem.ResourceId, - Title = node.ResourceItem.Title, - //ApplicationContext = node.ApplicationContext, - //ModuleContext = node.ModuleContext, + ResourceId = node.ResourceContext.ResourceId, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()), Instance = CreateInstance(node, new UriResource(outPathSegments.ToArray()), searchContext), }; } - else if (node.IsLeaf && nextPathSegment != null && node.ResourceItem != null && node.ResourceItem.IncludeSubPaths) + else if (node.IsLeaf && nextPathSegment != null && node.ResourceContext != null && node.ResourceContext.IncludeSubPaths) { return new SearchResult() { - Id = node.ResourceItem.ResourceId, - Title = node.ResourceItem.Title, - //ApplicationContext = node.ApplicationContext, - //ModuleContext = node.ModuleContext, + ResourceId = node.ResourceContext.ResourceId, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()), @@ -420,8 +500,6 @@ SearchContext searchContext // 404 return new SearchResult() { - //ApplicationContext = node.ApplicationContext, - //ModuleContext = node.ModuleContext, ResourceContext = node.ResourceContext, SearchContext = searchContext, Uri = new UriResource(outPathSegments.ToArray()) @@ -435,44 +513,19 @@ SearchContext searchContext /// The uri. /// The search context. /// The instance or null. - private static IResource CreateInstance(SitemapNode node, UriResource uri, SearchContext context) + private IResource CreateInstance(SitemapNode node, UriResource uri, SearchContext context) { - if (node == null || node.ResourceItem == null || node.ResourceContext == null) + if (node == null || node.ResourceContext == null) { return null; } - if (node.ResourceContext.Cache && node.Instance != null) - { - return node.Instance; - } - - var instance = Activator.CreateInstance(node.ResourceItem.ResourceClass) as IResource; + var instance = _resourceManager.CreateResourceInstance(node.ResourceContext, context.Culture); - if (instance is II18N i18n) - { - i18n.Culture = context.Culture; - } - - //if (instance is Resource resorce) + //if (instance is IPage page) //{ - //resorce.Id = node.ResourceItem?.ResourceId; - //resorce.ApplicationContext = node.ResourceContext?.ModuleContext?.ApplicationContext; - //resorce.ModuleContext = node.ResourceContext?.ModuleContext; //} - if (instance is IPage page) - { - page.Title = node.ResourceItem?.Title; - } - - instance.Initialization(node.ResourceContext); - - if (node.ResourceContext.Cache) - { - node.Instance = instance; - } - return instance; } @@ -489,7 +542,7 @@ private static bool IsMatched(SitemapNode node, string pathSegement) return false; } - return node.PathSegment.IsMatched(pathSegement); + return node.PathSegment?.IsMatched(pathSegement) ?? false; } /// @@ -502,19 +555,19 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in { output.Add ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:sitemapmanager.sitemap" ) ); - var preorder = Root + var preorder = _root .GetPreOrder() - .Select(x => InternationalizationManager.I18N + .Select(x => I18N.Translate ( "webexpress:sitemapmanager.preorder", " " + x.ToString().PadRight(60), - x.ResourceItem?.ResourceId ?? "" + x.ResourceContext?.ResourceId ?? "" )); foreach (var node in preorder) @@ -522,5 +575,14 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add(node); } } + + /// + /// Returns a string that represents the current sitemap. + /// + /// A string that represents the current sitemap. + public override string ToString() + { + return string.Join(" | ", _root.GetPreOrder()); + } } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs index 042ff53..a014c1f 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs @@ -15,31 +15,11 @@ public class SitemapNode /// public IUriPathSegment PathSegment { get; internal set; } - /// - /// Returns the resource item. - /// - public ResourceItem ResourceItem { get; internal set; } - - ///// - ///// Returns the context of the application. - ///// - //public IApplicationContext ApplicationContext { get; internal set; } - - ///// - ///// Returns the context of the module. - ///// - //public IModuleContext ModuleContext { get; internal set; } - /// /// Returns the context of the resource. /// public IResourceContext ResourceContext { get; internal set; } - /// - /// Returns the instance - /// - public IResource Instance { get; internal set; } - /// /// Returns the child nodes. /// @@ -147,11 +127,7 @@ public SitemapNode Copy() var node = new SitemapNode() { PathSegment = PathSegment, - ResourceItem = ResourceItem, - //ApplicationContext = ApplicationContext, - //ModuleContext = ModuleContext, ResourceContext = ResourceContext, - Instance = Instance, Parent = Parent, Children = Children.Select(x => x.Copy()).ToList() }; diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index cb0f0a6..092f9eb 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -13,8 +13,13 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Management of status pages. /// - public class StatusPageManager : IComponentPlugin, ISystemComponent + public class StatusPageManager : IManagerPlugin, ISystemComponent { + private readonly IComponentManager _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly StatusPageDictionary _dictionary = []; + private readonly StatusPageDictionaryItem _defaults = []; + /// /// An event that fires when an status page is added. /// @@ -25,30 +30,10 @@ public class StatusPageManager : IComponentPlugin, ISystemComponent /// public event EventHandler RemoveStatusPage; - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the directory where the status pages are listed. - /// - private StatusPageDictionary Dictionary { get; } = new StatusPageDictionary(); - - /// - /// Returns the default Items. - /// - private StatusPageDictionaryItem Defaults { get; } = new StatusPageDictionaryItem(); - - /// - /// Returns or sets the component manager. - /// - private ComponentManager ComponentManager { get; set; } - /// /// Returns all status pages. /// - public IEnumerable StatusPages => Dictionary.Values + public IEnumerable StatusPages => _dictionary.Values .SelectMany(x => x.Values) .Select(x => new StatusPageContext() { @@ -63,32 +48,26 @@ public class StatusPageManager : IComponentPlugin, ISystemComponent /// Initializes a new instance of the class. /// /// The component manager. - internal StatusPageManager(ComponentManager componentManager) + /// The reference to the context of the host. + internal StatusPageManager(IComponentManager componentManager, IHttpServerContext httpServerContext) { - ComponentManager = componentManager; + _componentManager = componentManager; - ComponentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - ComponentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - } - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:statuspagemanager.initialization") + I18N.Translate("webexpress:statuspagemanager.initialization") ); } @@ -143,12 +122,12 @@ public void Register(IPluginContext pluginContext) if (statusCode > 0) { - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { - Dictionary.Add(pluginContext, new StatusPageDictionaryItem()); + _dictionary.Add(pluginContext, new StatusPageDictionaryItem()); } - var item = Dictionary[pluginContext]; + var item = _dictionary[pluginContext]; if (!item.ContainsKey(statusCode)) { item.Add(statusCode, new StatusPageItem() @@ -158,49 +137,45 @@ public void Register(IPluginContext pluginContext) StatusPageClass = resource, PluginContext = pluginContext, Title = title, - Icon = new UriResource(icon), - //ModuleId = moduleId + Icon = new UriResource(icon) }); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:statuspagemanager.register", statusCode, - //moduleId, resource.Name ) ); } else { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:statuspagemanager.duplicat", statusCode, - //moduleId, resource.Name ) ); } // default - if (!Defaults.ContainsKey(statusCode)) + if (!_defaults.ContainsKey(statusCode)) { - Defaults.Add(statusCode, new StatusPageItem() + _defaults.Add(statusCode, new StatusPageItem() { Id = id, StatusCode = statusCode, StatusPageClass = resource, - PluginContext = pluginContext, - //ModuleId = moduleId + PluginContext = pluginContext }); } else if (defaultItem) { - Defaults[statusCode] = new StatusPageItem() + _defaults[statusCode] = new StatusPageItem() { Id = id, StatusCode = statusCode, @@ -213,12 +188,11 @@ public void Register(IPluginContext pluginContext) } else { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( - InternationalizationManager.I18N + I18N.Translate ( "webexpress:statuspagemanager.statuscode", - //moduleId, resource.Name ) ); @@ -250,9 +224,9 @@ internal IEnumerable GetStatusCodes(IPluginContext pluginContext) return Enumerable.Empty(); } - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.ContainsKey(pluginContext)) { - return Dictionary[pluginContext].Keys; + return _dictionary[pluginContext].Keys; } return Enumerable.Empty(); @@ -265,17 +239,17 @@ internal IEnumerable GetStatusCodes(IPluginContext pluginContext) /// The first status page found to the given states or null. private StatusPageItem GetStatusPage(int status) { - if (Defaults == null) + if (_defaults == null) { return null; } - if (!Defaults.ContainsKey(status)) + if (!_defaults.ContainsKey(status)) { return null; } - return Defaults[status]; + return _defaults[status]; } /// @@ -291,17 +265,17 @@ private StatusPageItem GetStatusPage(int status, IPluginContext pluginContext) return null; } - if (!Dictionary.ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { return null; } - if (!Dictionary[pluginContext].ContainsKey(status)) + if (!_dictionary[pluginContext].ContainsKey(status)) { return null; } - return Dictionary[pluginContext][status]; + return _dictionary[pluginContext][status]; } /// @@ -369,7 +343,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in output.Add ( string.Empty.PadRight(4) + - InternationalizationManager.I18N + I18N.Translate ( "webexpress:statuspagemanager.statuspage", statusCode diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 483d4f9..8d5b844 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebTask /// /// Management of ad-hoc tasks. /// - public class TaskManager : IComponent, ISystemComponent + public class TaskManager : IManager, ISystemComponent { /// /// Returns or sets the reference to the context of the host. @@ -38,7 +38,7 @@ public void Initialization(IHttpServerContext context) HttpServerContext.Log.Debug ( - InternationalizationManager.I18N("webexpress:applicationmanager.initialization") + I18N.Translate("webexpress:applicationmanager.initialization") ); } diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs index 1403606..5503b11 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs @@ -107,7 +107,7 @@ public virtual bool Equals(IUriPathSegment obj) /// The culture. public virtual string GetDisplay(CultureInfo culture) { - return InternationalizationManager.I18N(culture, Display); + return I18N.Translate(culture, Display); } /// diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs index 9db21b9..7552a8c 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs @@ -93,7 +93,7 @@ public virtual bool Equals(IUriPathSegment obj) /// The culture. public virtual string GetDisplay(CultureInfo culture) { - return InternationalizationManager.I18N(culture, Display); + return I18N.Translate(culture, Display); } /// diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs index e84749c..88325b1 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariable.cs @@ -131,7 +131,7 @@ public virtual bool Equals(IUriPathSegment obj) /// The culture. public virtual string GetDisplay(CultureInfo culture) { - return string.Format(InternationalizationManager.I18N(culture, Display), Value); + return string.Format(I18N.Translate(culture, Display), Value); } /// diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs index baa0f5b..ee8af60 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs @@ -114,7 +114,7 @@ public override string GetDisplay(CultureInfo culture) return string.Format ( - InternationalizationManager.I18N(culture, Display), + I18N.Translate(culture, Display), guid ); } From 3d0579baf47d8d0f24a14173908225cfb9ee8cd8 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 7 Oct 2024 23:19:31 +0200 Subject: [PATCH 019/162] restructured - split pages, resources, and rest api into separate components --- .../Fixture/ResourceCounter.cs | 49 -- .../Fixture/ResourceMonitor.cs | 51 -- .../Fixture/UnitTestControlFixture.cs | 37 +- .../Manager/UnitTestApplication.cs | 78 ++- .../Manager/UnitTestComponent.cs | 12 +- .../Manager/UnitTestInternationalization.cs | 28 +- .../Manager/UnitTestModule.cs | 48 +- .../Manager/UnitTestPageManager.cs | 121 ++++ .../Manager/UnitTestPlugin.cs | 107 ++-- .../Manager/UnitTestResourceManager.cs | 86 +-- .../Manager/UnitTestSitemapManager.cs | 48 +- src/WebExpress.WebCore.Test/TestPage.cs | 91 --- src/WebExpress.WebCore.Test/TestPageA1X.cs | 65 +++ .../TestResourceA1X.cs | 5 +- .../TestResourceA1Y.cs | 4 +- .../TestResourceA2X.cs | 5 +- .../TestResourceAB1X.cs | 5 +- src/WebExpress.WebCore/HttpServer.cs | 18 +- .../Internationalization/I18N.cs | 20 +- .../IInternationalizationManager.cs | 2 +- .../InternationalizationExtensions.cs | 12 +- .../InternationalizationManager.cs | 6 +- .../Internationalization/de | 6 + .../Internationalization/en | 14 +- .../WebApplication/ApplicationManager.cs | 6 +- .../WebApplication/IApplicationManager.cs | 2 +- .../WebComponent/ComponentActivator.cs | 8 +- .../{ComponentManager.cs => ComponentHub.cs} | 71 ++- .../WebComponent/ComponentItem.cs | 2 +- .../WebComponent/IComponentHub.cs | 142 +++++ .../WebComponent/IComponentManager.cs | 132 +---- ...erPlugin.cs => IComponentManagerPlugin.cs} | 2 +- .../WebComponent/IEndpoint.cs | 9 + .../WebComponent/IEndpointContext.cs | 66 +++ .../{IManager.cs => IEndpointManager.cs} | 5 +- .../WebEvent/EventManager.cs | 6 +- src/WebExpress.WebCore/WebEx.cs | 127 +--- src/WebExpress.WebCore/WebJob/JobManager.cs | 6 +- src/WebExpress.WebCore/WebLog/LogManager.cs | 2 +- src/WebExpress.WebCore/WebMessage/Request.cs | 2 +- .../WebModule/IModuleContext.cs | 3 +- .../WebModule/IModuleManager.cs | 2 +- .../WebModule/ModuleManager.cs | 6 +- .../WebPackage/PackageManager.cs | 8 +- src/WebExpress.WebCore/WebPage/IPage.cs | 25 +- .../WebPage/IPageContext.cs | 15 + .../WebPage/IPageManager.cs | 91 +++ .../WebPage/IRenderContext.cs | 42 ++ src/WebExpress.WebCore/WebPage/IVisualTree.cs | 4 +- .../WebPage/IVisualTreeContext.cs | 37 ++ .../WebPage/Model/PageDictionary.cs | 13 + .../WebPage/Model/PageItem.cs | 238 ++++++++ src/WebExpress.WebCore/WebPage/Page.cs | 40 +- src/WebExpress.WebCore/WebPage/PageContext.cs | 117 ++++ src/WebExpress.WebCore/WebPage/PageManager.cs | 541 ++++++++++++++++++ .../RedirectException.cs | 2 +- .../WebPage/RenderContext.cs | 49 +- src/WebExpress.WebCore/WebPage/VisualTree.cs | 6 +- .../WebPage/VisualTreeContext.cs | 59 ++ .../WebPlugin/IPluginManager.cs | 2 +- .../WebPlugin/PluginManager.cs | 4 +- .../WebResource/IResource.cs | 19 +- .../WebResource/IResourceContext.cs | 67 +-- .../WebResource/IResourceManager.cs | 21 +- .../{ => Model}/ResourceDictionary.cs | 2 +- .../WebResource/{ => Model}/ResourceItem.cs | 8 +- .../WebResource/ResourceAsset.cs | 13 +- .../WebResource/ResourceContext.cs | 15 +- .../WebResource/ResourceManager.cs | 104 ++-- .../WebSession/SessionManager.cs | 2 +- .../WebSitemap/EndpointRegistration.cs | 36 ++ .../WebSitemap/ISitemapManager.cs | 26 +- .../WebSitemap/SearchResult.cs | 37 +- .../WebSitemap/SitemapManager.cs | 190 +++--- .../WebSitemap/SitemapNode.cs | 12 +- .../WebStatusPage/IStatusPage.cs | 6 +- .../WebStatusPage/StatusPageManager.cs | 16 +- src/WebExpress.WebCore/WebTask/Task.cs | 4 +- src/WebExpress.WebCore/WebTask/TaskManager.cs | 2 +- 79 files changed, 2345 insertions(+), 1045 deletions(-) delete mode 100644 src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs delete mode 100644 src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs delete mode 100644 src/WebExpress.WebCore.Test/TestPage.cs create mode 100644 src/WebExpress.WebCore.Test/TestPageA1X.cs rename src/WebExpress.WebCore/WebComponent/{ComponentManager.cs => ComponentHub.cs} (87%) create mode 100644 src/WebExpress.WebCore/WebComponent/IComponentHub.cs rename src/WebExpress.WebCore/WebComponent/{IManagerPlugin.cs => IComponentManagerPlugin.cs} (94%) create mode 100644 src/WebExpress.WebCore/WebComponent/IEndpoint.cs create mode 100644 src/WebExpress.WebCore/WebComponent/IEndpointContext.cs rename src/WebExpress.WebCore/WebComponent/{IManager.cs => IEndpointManager.cs} (50%) create mode 100644 src/WebExpress.WebCore/WebPage/IPageContext.cs create mode 100644 src/WebExpress.WebCore/WebPage/IPageManager.cs create mode 100644 src/WebExpress.WebCore/WebPage/IRenderContext.cs create mode 100644 src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs create mode 100644 src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs create mode 100644 src/WebExpress.WebCore/WebPage/Model/PageItem.cs create mode 100644 src/WebExpress.WebCore/WebPage/PageContext.cs create mode 100644 src/WebExpress.WebCore/WebPage/PageManager.cs rename src/WebExpress.WebCore/{WebResource => WebPage}/RedirectException.cs (95%) create mode 100644 src/WebExpress.WebCore/WebPage/VisualTreeContext.cs rename src/WebExpress.WebCore/WebResource/{ => Model}/ResourceDictionary.cs (87%) rename src/WebExpress.WebCore/WebResource/{ => Model}/ResourceItem.cs (97%) create mode 100644 src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs b/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs deleted file mode 100644 index 4c6de61..0000000 --- a/src/WebExpress.WebCore.Test/Fixture/ResourceCounter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using WebExpress.WebCore.WebResource; - -namespace WebExpress.WebCore.Test.Fixture -{ - /// - /// The class manages a list of objects. - /// - internal static class ResourceCounter - { - private static List _resources = new List(); - - /// - /// Returns the number of resources. - /// - public static int Count => ResourceCounter.Resources.Count(); - - /// - /// Returns the component manager. - /// - public static IEnumerable Resources => _resources; - - /// - /// Adds a resource to the monitor. - /// - /// The resource to be added. - public static void Add(IResource resource) - { - _resources.Add(resource); - } - - /// - /// Checks if a resource of the same type exists. - /// - /// The type of resource to check for. - /// true if a resource of the same type exists; otherwise, false. - public static bool Contains() where T : IResource - { - return _resources.Any(resource => resource is T); - } - - /// - /// Clears the list of resources. - /// - public static void Clear() - { - _resources.Clear(); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs b/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs deleted file mode 100644 index 566d92d..0000000 --- a/src/WebExpress.WebCore.Test/Fixture/ResourceMonitor.cs +++ /dev/null @@ -1,51 +0,0 @@ -using WebExpress.WebCore.WebResource; - -namespace WebExpress.WebCore.Test.Fixture -{ - /// - /// The class manages the component managers and disposes them when the using statement is exited. - /// - internal class ResourceMonitor : IDisposable - { - /// - /// Returns the number of resources. - /// - public int Count => ResourceCounter.Resources.Count(); - - /// - /// Initializes a new instance of the class with the specified component manager. - /// - public ResourceMonitor() - { - ResourceCounter.Clear(); - } - - /// - /// Checks if a resource of the same type exists. - /// - /// The type of resource to check for. - /// true if a resource of the same type exists; otherwise, false. - public bool Contains(Type type) - { - return ResourceCounter.Resources.Any(resource => resource.Equals(type)); - } - - /// - /// Checks if a resource of the same type exists. - /// - /// The type of resource to check for. - /// true if a resource of the same type exists; otherwise, false. - public bool Contains() where T : IResource - { - return ResourceCounter.Resources.Any(resource => resource is T); - } - - /// - /// Disposes all resources. - /// - public void Dispose() - { - ResourceCounter.Clear(); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index cea31d7..58aa0cb 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -50,12 +50,12 @@ public static IHttpServerContext CreateHttpServerContext() } /// - /// Create a component manager. + /// Create a component hub. /// /// The component manager. - public static ComponentManager CreateComponentManager() + public static ComponentHub CreateComponentHub() { - var ctorComponentManager = typeof(ComponentManager).GetConstructor + var ctorComponentManager = typeof(ComponentHub).GetConstructor ( BindingFlags.NonPublic | BindingFlags.Instance, null, @@ -63,11 +63,11 @@ public static ComponentManager CreateComponentManager() null ); - var componentManager = (ComponentManager)ctorComponentManager.Invoke([CreateHttpServerContext()]); + var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContext()]); // set static field in the webex class var type = typeof(WebEx); - var field = type.GetField("_componentManager", BindingFlags.Static | BindingFlags.NonPublic); + var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, componentManager); @@ -75,12 +75,12 @@ public static ComponentManager CreateComponentManager() } /// - /// Create a component manager. + /// Create a component hub and register the plugins. /// /// The component manager. - public static ComponentManager CreateAndRegisterComponentManager() + public static ComponentHub CreateAndRegisterComponentHub() { - var componentManager = CreateComponentManager(); + var componentManager = CreateComponentHub(); var pluginManager = componentManager.PluginManager as PluginManager; pluginManager.Register(); @@ -165,7 +165,7 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") featureCollection.Set(requestIdentifierFeature); featureCollection.Set(connectionFeature); - var componentManager = CreateComponentManager(); + var componentManager = CreateComponentHub(); var context = new WebMessage.HttpContext(featureCollection, componentManager.HttpServerContext); return context; @@ -178,29 +178,26 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") public static RenderContext CrerateContext() { var request = CrerateRequest(); - var page = new TestPage(); - var visualTree = new VisualTree(); + var page = new TestPageA1X(CreratePageContext()); - page.Initialization(CrerateResourceContext()); - - return new RenderContext(page, request, visualTree); + return new RenderContext(page, CreratePageContext(), request); } /// - /// Create a fake resource context. + /// Create a fake page context. /// /// A fake context for testing. - public static ResourceContext CrerateResourceContext() + public static PageContext CreratePageContext() { - var ctorResourceContext = typeof(ResourceContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); + var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); - var moduleContext = WebEx.ComponentManager.ModuleManager.Modules + var moduleContext = WebEx.ComponentHub.ModuleManager.Modules .Where(x => x.ModuleId == typeof(TestModuleA1).FullName.ToLower()) .FirstOrDefault(); - var resourceContext = (ResourceContext)ctorResourceContext.Invoke([moduleContext]); + var pageContext = (PageContext)ctorPageContext.Invoke([moduleContext]); - return resourceContext; + return pageContext; } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index 84e88ac..a519425 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager @@ -17,16 +18,16 @@ public class UnitTestApplication public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; // test execution pluginManager.Register(); - Assert.Equal(3, componentManager.ApplicationManager.Applications.Count()); - Assert.Equal("webexpress.webcore.test.testapplicationa", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationb", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationc", componentManager.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); + Assert.Equal(3, componentHub.ApplicationManager.Applications.Count()); + Assert.Equal("webexpress.webcore.test.testapplicationa", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationb", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationc", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); } /// @@ -36,14 +37,14 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applicationManager = componentManager.ApplicationManager as ApplicationManager; - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applicationManager = componentHub.ApplicationManager as ApplicationManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution applicationManager.Remove(plugin); - Assert.Empty(componentManager.ApplicationManager.Applications); + Assert.Empty(componentHub.ApplicationManager.Applications); } /// @@ -56,8 +57,8 @@ public void Remove() public void Id(Type applicationType, string id) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(id, applcation.ApplicationId); @@ -73,8 +74,8 @@ public void Id(Type applicationType, string id) public void Name(Type applicationType, string name) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(name, applcation.ApplicationName); @@ -90,8 +91,8 @@ public void Name(Type applicationType, string name) public void Description(Type applicationType, string description) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(description, applcation.Description); @@ -107,8 +108,8 @@ public void Description(Type applicationType, string description) public void Icon(Type applicationType, string icon) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(icon, applcation.Icon); @@ -124,8 +125,8 @@ public void Icon(Type applicationType, string icon) public void ContextPath(Type applicationType, string contextPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(contextPath, applcation.ContextPath); @@ -141,8 +142,8 @@ public void ContextPath(Type applicationType, string contextPath) public void AssetPath(Type applicationType, string assetPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(assetPath, applcation.AssetPath); @@ -158,11 +159,40 @@ public void AssetPath(Type applicationType, string assetPath) public void DataPath(Type applicationType, string dataPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var applcation = componentManager.ApplicationManager.GetApplcation(applicationType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); // test execution Assert.Equal(dataPath, applcation.DataPath); } + + /// + /// Tests whether the application manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ApplicationManager.GetType())); + } + + /// + /// Tests whether the application context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var application in componentHub.ApplicationManager.Applications) + { + Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Application context {application.GetType().Name} does not implement IContext."); + } + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs index 0cc7199..b01ab3d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs @@ -15,10 +15,10 @@ public class UnitTestComponent public void PluginManager() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); + var componentHub = UnitTestControlFixture.CreateComponentHub(); // test execution - Assert.NotNull(componentManager.PluginManager); + Assert.NotNull(componentHub.PluginManager); } /// @@ -28,10 +28,10 @@ public void PluginManager() public void ApplicationManager() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); + var componentHub = UnitTestControlFixture.CreateComponentHub(); // test execution - Assert.NotNull(componentManager.ApplicationManager); + Assert.NotNull(componentHub.ApplicationManager); } /// @@ -41,10 +41,10 @@ public void ApplicationManager() public void ModuleManager() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); + var componentHub = UnitTestControlFixture.CreateComponentHub(); // test execution - Assert.NotNull(componentManager.ModuleManager); + Assert.NotNull(componentHub.ModuleManager); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index a38b473..2c57027 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -1,6 +1,7 @@ using System.Globalization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager @@ -18,8 +19,8 @@ public class UnitTestInternationalization public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; // test execution pluginManager.Register(); @@ -34,9 +35,9 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var internationalizationManager = componentManager.InternationalizationManager as InternationalizationManager; - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var internationalizationManager = componentHub.InternationalizationManager as InternationalizationManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution internationalizationManager.Remove(plugin); @@ -51,7 +52,7 @@ public void Remove() public void GetDefaultCulture() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); @@ -71,7 +72,7 @@ public void GetDefaultCulture() public void Translate(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + UnitTestControlFixture.CreateAndRegisterComponentHub(); if (cultureName == null && !param.Any()) { @@ -117,5 +118,18 @@ public void Translate(string key, string excepted, string cultureName = null, st } } + + /// + /// Tests whether the internationalization manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.InternationalizationManager.GetType())); + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs index fca4ad7..afa9239 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -17,7 +18,7 @@ public class UnitTestModule public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); + var componentManager = UnitTestControlFixture.CreateComponentHub(); var pluginManager = componentManager.PluginManager as PluginManager; // test execution @@ -33,7 +34,7 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var moduleManager = componentManager.ModuleManager as ModuleManager; var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -53,7 +54,7 @@ public void Remove() public void Id(Type applicationType, Type moduleType, string id) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -72,7 +73,7 @@ public void Id(Type applicationType, Type moduleType, string id) public void Name(Type applicationType, Type moduleType, string name) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -91,7 +92,7 @@ public void Name(Type applicationType, Type moduleType, string name) public void Description(Type applicationType, Type moduleType, string description) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -112,7 +113,7 @@ public void Description(Type applicationType, Type moduleType, string descriptio public void Icon(Type applicationType, Type moduleType, string icon) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -133,7 +134,7 @@ public void Icon(Type applicationType, Type moduleType, string icon) public void ContextPath(Type applicationType, Type moduleType, string contextPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -154,7 +155,7 @@ public void ContextPath(Type applicationType, Type moduleType, string contextPat public void AssetPath(Type applicationType, Type moduleType, string assetPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -175,11 +176,40 @@ public void AssetPath(Type applicationType, Type moduleType, string assetPath) public void DataPath(Type applicationType, Type moduleType, string dataPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); // test execution Assert.Equal(dataPath, module.DataPath); } + + /// + /// Tests whether the module manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentManager.ModuleManager.GetType())); + } + + /// + /// Tests whether the module context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var module in componentManager.ModuleManager.Modules) + { + Assert.True(typeof(IContext).IsAssignableFrom(module.GetType()), $"Module context {module.GetType().Name} does not implement IContext."); + } + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs new file mode 100644 index 0000000..23d3b94 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -0,0 +1,121 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the page manager. + /// + [Collection("NonParallelTests")] + public class UnitTestPageManager + { + /// + /// Test the register function of the page manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.Equal(1, componentHub.PageManager.Pages.Count()); + } + + /// + /// Test the remove function of the page manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var pageManager = componentHub.PageManager as PageManager; + + // test execution + pageManager.Remove(plugin); + + Assert.Empty(componentHub.PageManager.Pages); + } + + /// + /// Test the id property of the page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webexpress.webcore.test.testpagea1x")] + public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var page = componentHub.PageManager.GetPage(module, resourceType); + + // test execution + Assert.Equal(id, page.EndpointId); + } + + /// + /// Test the title property of the page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webindex:homepage.label")] + + public void Title(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var page = componentHub.PageManager.GetPage(module, resourceType); + + // test execution + Assert.Equal(id, page.PageTitle); + } + + /// + /// Test the context path property of the page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "/aca/mca")] + public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var page = componentHub.PageManager.GetPage(module, resourceType); + + // test execution + Assert.Equal(id, page.ContextPath); + } + + /// + /// Tests whether the page manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.PageManager.GetType())); + } + + /// + /// Tests whether the page context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var application in componentHub.PageManager.Pages) + { + Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Page context {application.GetType().Name} does not implement IContext."); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index 1ee521d..b198524 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test.Manager @@ -16,14 +17,14 @@ public class UnitTestPlugin public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; // test execution pluginManager.Register(); - Assert.Single(componentManager.PluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); + Assert.Single(componentHub.PluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); } /// @@ -33,18 +34,18 @@ public void Register() public void RegisterEvent() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; var i = 0; var triggered = false; - componentManager.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; + componentHub.PluginManager.AddPlugin += (s, e) => { i++; triggered = true; }; // test execution pluginManager.Register(); - Assert.Single(componentManager.PluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", componentManager.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); + Assert.Single(componentHub.PluginManager.Plugins); + Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); Assert.Equal(1, i); Assert.True(triggered); } @@ -56,15 +57,15 @@ public void RegisterEvent() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution pluginManager.Remove(plugin); - Assert.Empty(componentManager.PluginManager.Plugins); + Assert.Empty(componentHub.PluginManager.Plugins); } /// @@ -74,19 +75,19 @@ public void Remove() public void RemoveEvent() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; var i = 1; var triggered = false; - componentManager.PluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; + componentHub.PluginManager.RemovePlugin += (s, e) => { i--; triggered = true; }; pluginManager.Register(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution pluginManager.Remove(plugin); - Assert.Empty(componentManager.PluginManager.Plugins); + Assert.Empty(componentHub.PluginManager.Plugins); Assert.Equal(0, i); Assert.True(triggered); } @@ -98,10 +99,10 @@ public void RemoveEvent() public void GetPluginById() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - var plugin = componentManager.PluginManager.GetPlugin("webexpress.webcore.test"); + var plugin = componentHub.PluginManager.GetPlugin("webexpress.webcore.test"); Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } @@ -113,10 +114,10 @@ public void GetPluginById() public void GetPluginByType() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); Assert.Equal("webexpress.webcore.test", plugin?.PluginId); } @@ -128,8 +129,8 @@ public void GetPluginByType() public void Id() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution Assert.Equal(typeof(TestPlugin).Namespace.ToLower(), plugin.PluginId); @@ -142,8 +143,8 @@ public void Id() public void GetName() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution Assert.Equal("TestPlugin", plugin.PluginName); @@ -156,8 +157,8 @@ public void GetName() public void GetDescription() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution Assert.Equal("plugin.description", plugin.Description); @@ -170,8 +171,8 @@ public void GetDescription() public void GetIcon() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution Assert.Equal("/assets/img/Logo.png", plugin.Icon); @@ -188,15 +189,15 @@ public void GetIcon() public void Boot(string pluginId, string expected) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); - var plugin = componentManager.PluginManager.GetPlugin(pluginId); + var plugin = componentHub.PluginManager.GetPlugin(pluginId); // test execution pluginManager.Boot(plugin); - Assert.Single(componentManager.PluginManager.Plugins); + Assert.Single(componentHub.PluginManager.Plugins); Assert.Equal(expected, plugin?.PluginId); } @@ -211,16 +212,46 @@ public void Boot(string pluginId, string expected) public void ShutDown(string pluginId, string expected) { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentManager(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); - var plugin = componentManager.PluginManager.GetPlugin(pluginId); + var plugin = componentHub.PluginManager.GetPlugin(pluginId); // test execution pluginManager.ShutDown(plugin); - Assert.Single(componentManager.PluginManager.Plugins); + Assert.Single(componentHub.PluginManager.Plugins); Assert.Equal(expected, plugin?.PluginId); } + + /// + /// Tests whether the plugin manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.PluginManager.GetType())); + } + + /// + /// Tests whether the plugin context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var plugin in componentHub.PluginManager.Plugins) + { + Assert.True(typeof(IContext).IsAssignableFrom(plugin.GetType()), $"Plugin context {plugin.GetType().Name} does not implement IContext."); + } + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index 0508692..1f9180a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebResource; namespace WebExpress.WebCore.Test.Manager @@ -16,11 +17,11 @@ public class UnitTestResourceManager public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - // resources (3 unique + 2 ambiguous) + pages (1) - Assert.Equal(6, componentManager.ResourceManager.Resources.Count()); + // resources (3 unique + 2 ambiguous) + Assert.Equal(5, componentHub.ResourceManager.Resources.Count()); } /// @@ -30,14 +31,14 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); - var resourceManager = componentManager.ResourceManager as ResourceManager; + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var resourceManager = componentHub.ResourceManager as ResourceManager; // test execution resourceManager.Remove(plugin); - Assert.Empty(componentManager.ResourceManager.Resources); + Assert.Empty(componentHub.ResourceManager.Resources); } /// @@ -53,63 +54,62 @@ public void Remove() public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - using var componentMoinitor = new ResourceMonitor(); - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var resource = componentHub.ResourceManager.GetResorce(module, resourceType); // test execution - var resource = componentManager.ResourceManager.GetResorce(module, resourceType); - - Assert.Equal(id, resource.ResourceId); - Assert.False(componentMoinitor.Contains(resourceType)); + Assert.Equal(id, resource.EndpointId); } /// - /// Test the title property of the resource. + /// Test the context path property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webindex:resourcea1x.label")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "TestResourceA1Y")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webindex:resourcea2x.label")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webindex:resourceab1x.label")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webindex:resourceab1x.label")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "/aca/mca/a1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "/aca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/aca/mcab")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/acb/mcab")] - public void Title(Type applicationType, Type moduleType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - using var componentMoinitor = new ResourceMonitor(); - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var resource = componentHub.ResourceManager.GetResorce(module, resourceType); // test execution - var resource = componentManager.ResourceManager.GetResorce(module, resourceType); - - Assert.Equal(id, resource.ResourceTitle); - Assert.False(componentMoinitor.Contains(resourceType)); + Assert.Equal(id, resource.ContextPath); } /// - /// Test the context path property of the resource. + /// Tests whether the resource manager implements interface IComponentManager. /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "/aca/mca/a1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "/aca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/aca/mcab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/acb/mcab")] - - public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + [Fact] + public void IsIComponentManager() { // preconditions - using var componentMoinitor = new ResourceMonitor(); - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - var resource = componentManager.ResourceManager.GetResorce(module, resourceType); + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); + } - Assert.Equal(id, resource.ContextPath); - Assert.False(componentMoinitor.Contains(resourceType)); + /// + /// Tests whether the resource context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var application in componentHub.ResourceManager.Resources) + { + Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Resource context {application.GetType().Name} does not implement IContext."); + } } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 56639cc..9253a7d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,4 +1,6 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSitemap; namespace WebExpress.WebCore.Test.Manager @@ -16,12 +18,12 @@ public class UnitTestSitemapManager public void Refresh() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(13, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(14, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -33,27 +35,26 @@ public void Refresh() [InlineData("http://localhost:8080/aca/a2x", "webexpress.webcore.test.testresourcea2x")] [InlineData("http://localhost:8080/aca/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] [InlineData("http://localhost:8080/acb/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] + [InlineData("http://localhost:8080/aca/mca/pa1x", "webexpress.webcore.test.testpagea1x")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) { // preconditions - using var componentMoinitor = new ResourceMonitor(); - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); var context = UnitTestControlFixture.CreateHttpContext(); - componentManager.SitemapManager.Refresh(); - var map = componentManager.SitemapManager.ToString(); + componentHub.SitemapManager.Refresh(); + var map = componentHub.SitemapManager.ToString(); // test execution - var searchResult = componentManager.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() + var searchResult = componentHub.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() { - HttpServerContext = componentManager.HttpServerContext, - Culture = componentManager.HttpServerContext.Culture, + HttpServerContext = componentHub.HttpServerContext, + Culture = componentHub.HttpServerContext.Culture, HttpContext = context }); - Assert.Equal(id, searchResult.ResourceId); - //Assert.Single(componentMoinitor.Contains(resourceType)); + Assert.Equal(id, searchResult.EndpointId); } /// @@ -64,21 +65,34 @@ public void SearchResource(string uri, string id) [InlineData(typeof(TestResourceA1Y), "/aca/mca/a1x/a1y")] [InlineData(typeof(TestResourceA2X), "/aca/a2x")] [InlineData(typeof(TestResourceAB1X), "/aca/mcab/ab1x")] + [InlineData(typeof(TestPageA1X), "/aca/mca/pa1x")] public void GetUri(Type resourceType, string expected) { // preconditions - using var componentMoinitor = new ResourceMonitor(); - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentManager(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); var context = UnitTestControlFixture.CreateHttpContext(); - componentManager.SitemapManager.Refresh(); - //var ressource = componentManager.ResourceManager.GetResorces(resourceType).FirstOrDefault(); + componentHub.SitemapManager.Refresh(); // test execution - var uri = componentManager.SitemapManager.GetUri(resourceType); + var uri = componentHub.SitemapManager.GetUri(resourceType); Assert.Equal(expected, uri?.ToString()); - Assert.False(componentMoinitor.Contains(resourceType)); + } + + /// + /// Tests whether the sitemap manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SitemapManager.GetType())); } } + } diff --git a/src/WebExpress.WebCore.Test/TestPage.cs b/src/WebExpress.WebCore.Test/TestPage.cs deleted file mode 100644 index cacdb23..0000000 --- a/src/WebExpress.WebCore.Test/TestPage.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.ComponentModel; -using System.Globalization; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebResource; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy class for testing purposes. - /// - [Title("webindex:homepage.label")] - [Segment(null, "webindex:homepage.label")] - [ContextPath(null)] - [Module] - public sealed class TestPage : IPage - { - /// - /// Returns or sets the title of the page. - /// - public string Title { get; set; } - - /// - /// Returns or sets the resource context. - /// - public IResourceContext ResourceContext { get; private set; } - - /// - /// Returns or sets the culture information. - /// - public CultureInfo Culture { get => CultureInfo.CurrentCulture; set => throw new NotImplementedException(); } - public ISite Site { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public event EventHandler Disposed; - - public void Dispose() - { - throw new NotImplementedException(); - } - - /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. - /// - /// The context of the resource. - public void Initialization(IResourceContext resourceContext) - { - ResourceContext = resourceContext; - } - - /// - /// Post-processes the request and response. - /// - /// The request. - /// The response. - /// The processed response. - public Response PostProcess(WebMessage.Request request, Response response) - { - return null; - } - - /// - /// Pre-processes the request. - /// - /// The request. - public void PreProcess(WebMessage.Request request) - { - - } - - /// - /// Processes the request. - /// - /// The request. - /// The processed response. - public Response Process(WebMessage.Request request) - { - return null; - } - - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { - - } - - } -} diff --git a/src/WebExpress.WebCore.Test/TestPageA1X.cs b/src/WebExpress.WebCore.Test/TestPageA1X.cs new file mode 100644 index 0000000..0139b0d --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestPageA1X.cs @@ -0,0 +1,65 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Segment("pa1x", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestPageA1X : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Instillation of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public TestPageA1X(IPageContext pageContext) + { + PageContext = pageContext; + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + public void Process(RenderContext context) + { + + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + throw new NotImplementedException(); + } + + public void Process(IRenderContext context) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceA1X.cs b/src/WebExpress.WebCore.Test/TestResourceA1X.cs index 4a1683c..c748481 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1X.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; @@ -25,8 +24,6 @@ public TestResourceA1X(IResourceContext resourceContext) { throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); } - - ResourceCounter.Add(this); } /// diff --git a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs index 612a5e3..00c762a 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; @@ -18,7 +17,6 @@ public sealed class TestResourceA1Y : IResource /// public TestResourceA1Y() { - ResourceCounter.Add(this); } /// diff --git a/src/WebExpress.WebCore.Test/TestResourceA2X.cs b/src/WebExpress.WebCore.Test/TestResourceA2X.cs index 58d9be7..2fb04b1 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA2X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA2X.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; @@ -32,8 +31,6 @@ public TestResourceA2X(IResourceManager resourceManager, IResourceContext resour { throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); } - - ResourceCounter.Add(this); } /// diff --git a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs index cb14cf8..dec72db 100644 --- a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; @@ -31,8 +30,6 @@ public TestResourceAB1X(IResourceContext resourceContext, IResourceManager resou { throw new ArgumentNullException(nameof(resourceContext), "Parameter cannot be null or empty."); } - - ResourceCounter.Add(this); } /// diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index d366373..b34407a 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -256,7 +256,7 @@ private Response HandleClient(HttpContext context) )); // search page in sitemap - var searchResult = WebEx.ComponentManager.SitemapManager.SearchResource(context.Uri, new SearchContext() + var searchResult = WebEx.ComponentHub.SitemapManager.SearchResource(context.Uri, new SearchContext() { Culture = culture, HttpContext = context, @@ -269,8 +269,8 @@ private Response HandleClient(HttpContext context) resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count)?.PathSegments) { ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments), - ApplicationRoot = new UriResource(request.Uri, searchResult.ResourceContext?.ModuleContext?.ApplicationContext?.ContextPath.PathSegments), - ModuleRoot = new UriResource(request.Uri, searchResult.ResourceContext?.ModuleContext?.ContextPath.PathSegments), + ApplicationRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ModuleContext?.ApplicationContext?.ContextPath.PathSegments), + ModuleRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ModuleContext?.ContextPath.PathSegments), ResourceRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) }; @@ -283,9 +283,7 @@ private Response HandleClient(HttpContext context) if (searchResult.Instance != null) { - searchResult.Instance?.PreProcess(request); - response = searchResult.Instance?.Process(request); - response = searchResult.Instance?.PostProcess(request, response); + response = searchResult.Process(request); if (searchResult.Instance is IPage) { @@ -467,12 +465,12 @@ private async Task SendResponseAsync(HttpContext context, Response response) if (searchResult != null) { - var statusPage = WebEx.ComponentManager.StatusPageManager.CreateStatusPage + var statusPage = WebEx.ComponentHub.StatusPageManager.CreateStatusPage ( massage, response.Status, - searchResult?.ResourceContext?.ModuleContext?.PluginContext ?? - searchResult?.ResourceContext?.ModuleContext?.ApplicationContext?.PluginContext + searchResult?.EndpointContext?.ModuleContext?.PluginContext ?? + searchResult?.EndpointContext?.ModuleContext?.ApplicationContext?.PluginContext ); if (statusPage == null) @@ -506,7 +504,7 @@ private async Task SendResponseAsync(HttpContext context, Response response) // ContextPath = new UriResource() //}; - resource.Initialization(new ResourceContext(resource.ModuleContext, WebEx.ResourceManager)); + resource.Initialization(new ResourceContext(resource.ModuleContext, WebEx.ComponentHub?.ResourceManager)); } return statusPage.Process(request); diff --git a/src/WebExpress.WebCore/Internationalization/I18N.cs b/src/WebExpress.WebCore/Internationalization/I18N.cs index 10021a6..f345d84 100644 --- a/src/WebExpress.WebCore/Internationalization/I18N.cs +++ b/src/WebExpress.WebCore/Internationalization/I18N.cs @@ -15,7 +15,7 @@ public static class I18N /// The value of the key in the current language. public static string Translate(string key) { - return WebEx.InternationalizationManager?.Translate(key) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(key) ?? key; } /// @@ -26,7 +26,7 @@ public static string Translate(string key) /// The value of the key in the current language. public static string Translate(string key, params object[] args) { - return WebEx.InternationalizationManager?.Translate(key, args) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(key, args) ?? key; } /// @@ -37,7 +37,7 @@ public static string Translate(string key, params object[] args) /// The value of the key in the current language. public static string Translate(II18N obj, string key) { - return WebEx.InternationalizationManager?.Translate(obj, key) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(obj, key) ?? key; } /// @@ -49,7 +49,7 @@ public static string Translate(II18N obj, string key) /// The value of the key in the current language. public static string Translate(II18N obj, string key, params object[] args) { - return WebEx.InternationalizationManager?.Translate(obj, key, args) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(obj, key, args) ?? key; } /// @@ -60,7 +60,7 @@ public static string Translate(II18N obj, string key, params object[] args) /// The value of the key in the current language. public static string Translate(Request request, string key) { - return WebEx.InternationalizationManager.Translate(request, key) ?? key; + return WebEx.ComponentHub?.InternationalizationManager.Translate(request, key) ?? key; } /// @@ -72,7 +72,7 @@ public static string Translate(Request request, string key) /// The value of the key in the current language. public static string Translate(Request request, string key, params object[] args) { - return WebEx.InternationalizationManager?.Translate(request, key, args) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(request, key, args) ?? key; } /// @@ -83,7 +83,7 @@ public static string Translate(Request request, string key, params object[] args /// The value of the key in the current language. public static string Translate(CultureInfo culture, string key) { - return WebEx.InternationalizationManager?.Translate(culture, key) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(culture, key) ?? key; } /// @@ -95,7 +95,7 @@ public static string Translate(CultureInfo culture, string key) /// The value of the key in the current language. public static string Translate(CultureInfo culture, string key, params object[] args) { - return WebEx.InternationalizationManager?.Translate(culture, key, args) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(culture, key, args) ?? key; } /// @@ -107,7 +107,7 @@ public static string Translate(CultureInfo culture, string key, params object[] /// The value of the key in the current language. public static string Translate(CultureInfo culture, string pluginId, string key) { - return WebEx.InternationalizationManager?.Translate(culture, pluginId, key) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(culture, pluginId, key) ?? key; } /// @@ -120,7 +120,7 @@ public static string Translate(CultureInfo culture, string pluginId, string key) /// The value of the key in the current language. public static string Translate(CultureInfo culture, string pluginId, string key, params object[] args) { - return WebEx.InternationalizationManager?.Translate(culture, pluginId, key, args) ?? key; + return WebEx.ComponentHub?.InternationalizationManager?.Translate(culture, pluginId, key, args) ?? key; } } } diff --git a/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs index 58504c9..955b069 100644 --- a/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Internationalization /// /// The interface of the internationalization manager. /// - public interface IInternationalizationManager : IManager + public interface IInternationalizationManager : IComponentManager { /// /// Translates a given key to the default language. diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 889ea45..2cd6a87 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -13,7 +13,7 @@ public static class InternationalizationExtensions /// The value of the key in the current language. public static string I18N(this II18N obj, string key) { - return WebEx.InternationalizationManager.Translate(obj, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj, key); } /// @@ -25,7 +25,7 @@ public static string I18N(this II18N obj, string key) /// The value of the key in the current language. public static string I18N(this II18N obj, string pluginId, string key) { - return WebEx.InternationalizationManager.Translate(obj.Culture, pluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, pluginId, key); } /// @@ -37,7 +37,7 @@ public static string I18N(this II18N obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this II18N obj, IApplicationContext applicationContext, string key) { - return WebEx.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); } /// @@ -49,7 +49,7 @@ public static string I18N(this II18N obj, IApplicationContext applicationContext /// The value of the key in the current language. public static string I18N(this RenderContext obj, string pluginId, string key) { - return WebEx.InternationalizationManager.Translate(obj.Culture, pluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, pluginId, key); } /// @@ -60,7 +60,7 @@ public static string I18N(this RenderContext obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, string key) { - return WebEx.InternationalizationManager.Translate(obj.Culture, obj?.PluginContext?.PluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.PageContext?.PluginContext?.PluginId, key); } /// @@ -72,7 +72,7 @@ public static string I18N(this RenderContext obj, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, IApplicationContext applicationContext, string key) { - return WebEx.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); } } } diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index c1cfdaa..fe264e5 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -12,9 +12,9 @@ namespace WebExpress.WebCore.Internationalization /// /// Internationalization /// - public sealed class InternationalizationManager : IInternationalizationManager, IManagerPlugin, ISystemComponent + public sealed class InternationalizationManager : IInternationalizationManager, IComponentManagerPlugin, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; /// /// Returns the default language. @@ -36,7 +36,7 @@ public sealed class InternationalizationManager : IInternationalizationManager, /// /// The component manager. /// The reference to the context of the host. - private InternationalizationManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + private InternationalizationManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 0d9d472..a96dfea 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -101,6 +101,12 @@ resourcemanager.sitemap=Inhalt der Sitemap für das Modul '{0}': resourcemanager.wrongtype=Der Type '{0}' implementiert die Schnittstelle '{1}' nicht. resourcemanager.resource=Ressource: '{0}' für das Modul '{1}' +pagemanager.initialization=Der Pagemanager wurde initialisiert. +pagemanager.moduleless=Die Seite '{0}' besitzt keine Angaben zum Modul. +pagemanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. +pagemanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. +pagemanager.resource=Seite: '{0}' für das Modul '{1}' + statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. statuspagemanager.register=Status {0} wurde registriert und der Statusseite '{1}' zugewiesen. statuspagemanager.duplicat=Der Status {0} wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index b4b4ebc..d066110 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -86,9 +86,9 @@ modulemanager.register=The module '{0}' has been assigned to the application '{1 modulemanager.duplicate=The module '{0}' has already been registered in the application '{1}'. modulemanager.applicationless=The module '{0}' does not have any information about the application. modulemanager.module=Module: '{0}' for application(s) '{1}' -pluginmanager.module.initialization=The module '{1}' of the application '{0}' has been initialized. -pluginmanager.module.processing.start=The module '{1}' of the application '{0}' is running. -pluginmanager.module.processing.end=The running of the module '{1}' of the application '{0}' has been stopped. +modulemanager.module.initialization=The module '{1}' of the application '{0}' has been initialized. +modulemanager.module.processing.start=The module '{1}' of the application '{0}' is running. +modulemanager.module.processing.end=The running of the module '{1}' of the application '{0}' has been stopped. resourcemanager.initialization=The resource manager has been initialized. resourcemanager.register={0} resource(s) have been assigned to the module '{1}'. @@ -99,7 +99,13 @@ resourcemanager.addresource.duplicate=The resource '{0}' of module '{1}' has alr resourcemanager.addresource.error=The resource '{0}' of the module '{1}' could not be added. resourcemanager.sitemap=Sitemap content resource '{0}' application: resourcemanager.wrongtype=The type '{0}' does not implement the interface '{1}'. -resourcemanager.statuspage=Resource: '{0}' for module '{1}' +resourcemanager.resource=Resource: '{0}' for module '{1}' + +pagemanager.initialization=The page manager has been initialized. +pagemanager.moduleless=The page '{0}' does not have any information about the module. +pagemanager.addresource=The page '{0}' has been registered in the module '{1}'. +pagemanager.addresource.duplicate=The page '{0}' of module '{1}' has already been added. +pagemanager.resource=Page: '{0}' for module '{1}' statuspagemanager.initialization=The status page manager has been initialized. statuspagemanager.register=Status {0} has been registered and assigned to the status page '{1}'. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index fda630f..12918a0 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -15,9 +15,9 @@ namespace WebExpress.WebCore.WebApplication /// /// Management of WebExpress applications. /// - public sealed class ApplicationManager : IApplicationManager, IManagerPlugin, IExecutableElements, ISystemComponent + public sealed class ApplicationManager : IApplicationManager, IComponentManagerPlugin, IExecutableElements, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly ApplicationDictionary _dictionary = []; @@ -41,7 +41,7 @@ public sealed class ApplicationManager : IApplicationManager, IManagerPlugin, IE /// /// The component manager. /// The reference to the context of the host. - private ApplicationManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + private ApplicationManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs index b5dca09..543f9f1 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebApplication /// /// Interface of the management of WebExpress applications. /// - public interface IApplicationManager : IManager + public interface IApplicationManager : IComponentManager { /// /// An event that fires when an application is added. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index fbbbfbb..e27f999 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -16,7 +16,7 @@ public static class ComponentActivator /// The type of the component to create. /// The component manager to use for dependency injection. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, ComponentManager componentManager) where T : class, IManager + public static T CreateInstance(Type componentType, ComponentHub componentManager) where T : class, IComponentManager { var flags = BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -30,7 +30,7 @@ public static T CreateInstance(Type componentType, ComponentManager component var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var parameterValues = parameters.Select(parameter => - parameter.ParameterType == typeof(IComponentManager) ? componentManager : + parameter.ParameterType == typeof(IComponentHub) ? componentManager : parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? @@ -56,7 +56,7 @@ public static T CreateInstance(Type componentType, ComponentManager component /// The context to pass to the component's constructor. /// The component manager to use for dependency injection. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, C context, IComponentManager componentManager) where T : class, IComponent where C : IContext + public static T CreateInstance(Type componentType, C context, IComponentHub componentManager) where T : class, IComponent where C : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -70,7 +70,7 @@ public static T CreateInstance(Type componentType, C context, IComponentMa var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var parameterValues = parameters.Select(parameter => - parameter.ParameterType == typeof(IComponentManager) ? componentManager : + parameter.ParameterType == typeof(IComponentHub) ? componentManager : parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : parameter.ParameterType == typeof(C) ? context : properties.Where(x => x.PropertyType == parameter.ParameterType) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs similarity index 87% rename from src/WebExpress.WebCore/WebComponent/ComponentManager.cs rename to src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 7b24deb..88116cb 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPackage; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebSession; @@ -20,24 +21,25 @@ namespace WebExpress.WebCore.WebComponent /// /// Central management of components. /// - public class ComponentManager : IComponentManager + public class ComponentHub : IComponentHub { private readonly InternationalizationManager _internationalizationManager; private readonly PluginManager _pluginManager; private readonly ApplicationManager _applicationManager; private readonly ModuleManager _moduleManager; private readonly ResourceManager _resourceManager; + private readonly PageManager _pageManager; private readonly SitemapManager _sitemapManager; /// /// An event that fires when an component is added. /// - public event EventHandler AddComponent; + public event EventHandler AddComponent; /// /// An event that fires when an component is removed. /// - public event EventHandler RemoveComponent; + public event EventHandler RemoveComponent; /// /// Returns the reference to the context of the host. @@ -52,17 +54,19 @@ public class ComponentManager : IComponentManager /// /// Returns all registered managers. /// - public IEnumerable Managers => new IManager[] + public IEnumerable Managers => new IComponentManager[] { LogManager, PackageManager, _pluginManager, - ApplicationManager, + _applicationManager, _moduleManager, + _sitemapManager, + _resourceManager, + _pageManager, EventManager, JobManager, StatusPageManager, - _sitemapManager, _internationalizationManager, SessionManager, TaskManager @@ -71,86 +75,92 @@ public class ComponentManager : IComponentManager /// /// Returns the log manager. /// - /// The instance of the log manager or null. + /// The instance of the log manager. public LogManager LogManager { get; private set; } /// /// Returns the package manager. /// - /// The instance of the package manager or null. + /// The instance of the package manager. public PackageManager PackageManager { get; private set; } /// /// Returns the plugin manager. /// - /// The instance of the plugin manager or null. + /// The instance of the plugin manager. public IPluginManager PluginManager => _pluginManager; /// /// Returns the application manager. /// - /// The instance of the application manager or null. + /// The instance of the application manager. public IApplicationManager ApplicationManager => _applicationManager; /// /// Returns the module manager. /// - /// The instance of the module manager or null. + /// The instance of the module manager. public IModuleManager ModuleManager => _moduleManager; /// /// Returns the event manager. /// - /// The instance of the event manager or null. + /// The instance of the event manager. public EventManager EventManager { get; private set; } /// /// Returns the job manager. /// - /// The instance of the job manager or null. + /// The instance of the job manager. public JobManager JobManager { get; private set; } /// /// Returns the status page manager. /// - /// The instance of the status page manager or null. + /// The instance of the status page manager. public StatusPageManager StatusPageManager { get; private set; } /// /// Returns the resource manager. /// - /// The instance of the resource manager or null. + /// The instance of the resource manager. public IResourceManager ResourceManager => _resourceManager; + /// + /// Returns the page manager. + /// + /// The instance of the page manager. + public IPageManager PageManager => _pageManager; + /// /// Returns the sitemap manager. /// - /// The instance of the sitemap manager or null. + /// The instance of the sitemap manager. public ISitemapManager SitemapManager => _sitemapManager; /// /// Returns the internationalization manager. /// - /// The instance of the internationalization manager or null. + /// The instance of the internationalization manager. public IInternationalizationManager InternationalizationManager => _internationalizationManager; /// /// Returns the session manager. /// - /// The instance of the session manager or null. + /// The instance of the session manager. public SessionManager SessionManager { get; private set; } /// /// Returns the task manager. /// - /// The instance of the task manager manager or null. + /// The instance of the task manager manager. public TaskManager TaskManager { get; private set; } /// /// Initializes a new instance of the class. /// /// The reference to the context of the host. - internal ComponentManager(IHttpServerContext httpServerContext) + internal ComponentHub(IHttpServerContext httpServerContext) { HttpServerContext = httpServerContext; @@ -161,11 +171,12 @@ internal ComponentManager(IHttpServerContext httpServerContext) _internationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; _moduleManager = CreateInstance(typeof(ModuleManager)) as ModuleManager; + _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; + _pageManager = CreateInstance(typeof(PageManager)) as PageManager; StatusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; - _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; SessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; @@ -192,20 +203,20 @@ internal ComponentManager(IHttpServerContext httpServerContext) /// /// The component class. /// The instance of the create and initialized component. - private IManager CreateInstance(Type componentType) + private IComponentManager CreateInstance(Type componentType) { if (componentType == null) { return null; } - else if (!componentType.GetInterfaces().Where(x => x == typeof(IManager)).Any()) + else if (!componentType.GetInterfaces().Where(x => x == typeof(IComponentManager)).Any()) { HttpServerContext.Log.Warning ( _internationalizationManager.Translate ( "webexpress:componentmanager.wrongtype", - componentType?.FullName, typeof(IManager).FullName + componentType?.FullName, typeof(IComponentManager).FullName ) ); @@ -214,7 +225,7 @@ private IManager CreateInstance(Type componentType) try { - return ComponentActivator.CreateInstance(componentType, this); + return ComponentActivator.CreateInstance(componentType, this); } catch (Exception ex) { @@ -229,7 +240,7 @@ private IManager CreateInstance(Type componentType) /// /// The id. /// The instance of the component or null. - public IManager GetComponent(string id) + public IComponentManager GetComponent(string id) { return Dictionary.Values .SelectMany(x => x) @@ -243,7 +254,7 @@ public IManager GetComponent(string id) /// /// The component class. /// The instance of the component or null. - public T GetComponent() where T : IManager + public T GetComponent() where T : IComponentManager { return (T)Dictionary.Values .SelectMany(x => x) @@ -269,7 +280,7 @@ internal void Register(IPluginContext pluginContext) Dictionary.Add(pluginContext, []); var componentItems = Dictionary[pluginContext]; - foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IManager).Name) != null)) + foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IComponentManager).Name) != null)) { var id = type.FullName?.ToLower(); @@ -432,7 +443,7 @@ public void Remove(IPluginContext pluginContext) /// Raises the AddComponent event. /// /// The component. - private void OnAddComponent(IManager component) + private void OnAddComponent(IComponentManager component) { AddComponent?.Invoke(null, component); } @@ -441,7 +452,7 @@ private void OnAddComponent(IManager component) /// Raises the RemoveComponent event. /// /// The component. - private void OnRemoveComponent(IManager component) + private void OnRemoveComponent(IComponentManager component) { RemoveComponent?.Invoke(null, component); } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs index f4307a6..5a7aada 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentItem.cs @@ -17,7 +17,7 @@ public class ComponentItem /// /// Returns the component instance or null if not already created. /// - public IManager ComponentInstance { get; internal set; } + public IComponentManager ComponentInstance { get; internal set; } /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs new file mode 100644 index 0000000..932d055 --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebJob; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPackage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebStatusPage; +using WebExpress.WebCore.WebTask; + +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Interface of the central management of manager components. + /// + public interface IComponentHub : IComponentManager + { + /// + /// An event that fires when an component is added. + /// + event EventHandler AddComponent; + + /// + /// An event that fires when an component is removed. + /// + event EventHandler RemoveComponent; + + /// + /// Returns the reference to the context of the host. + /// + IHttpServerContext HttpServerContext { get; } + + /// + /// Returns all registered components. + /// + IEnumerable Managers { get; } + + /// + /// Returns the log manager. + /// + /// The instance of the log manager. + LogManager LogManager { get; } + + /// + /// Returns the package manager. + /// + /// The instance of the package manager. + PackageManager PackageManager { get; } + + /// + /// Returns the plugin manager. + /// + /// The instance of the plugin manager. + public IPluginManager PluginManager { get; } + /// + /// Returns the application manager. + /// + /// The instance of the application manager. + IApplicationManager ApplicationManager { get; } + + /// + /// Returns the module manager. + /// + /// The instance of the module manager. + IModuleManager ModuleManager { get; } + + /// + /// Returns the event manager. + /// + /// The instance of the event manager. + EventManager EventManager { get; } + + /// + /// Returns the job manager. + /// + /// The instance of the job manager. + JobManager JobManager { get; } + + /// + /// Returns the status page manager. + /// + /// The instance of the status page manager. + StatusPageManager StatusPageManager { get; } + + /// + /// Returns the resource manager. + /// + /// The instance of the resource manager. + IResourceManager ResourceManager { get; } + + /// + /// Returns the page manager. + /// + /// The instance of the page manager. + IPageManager PageManager { get; } + + /// + /// Returns the sitemap manager. + /// + /// The instance of the sitemap manager. + ISitemapManager SitemapManager { get; } + + /// + /// Returns the internationalization manager. + /// + /// The instance of the internationalization manager. + IInternationalizationManager InternationalizationManager { get; } + + /// + /// Returns the session manager. + /// + /// The instance of the session manager. + SessionManager SessionManager { get; } + + /// + /// Returns the task manager. + /// + /// The instance of the task manager manager. + TaskManager TaskManager { get; } + + /// + /// Returns a component based on its id. + /// + /// The id. + /// The instance of the component. + IComponentManager GetComponent(string id); + + /// + /// Returns a component based on its type. + /// + /// The component class. + /// The instance of the component. + T GetComponent() where T : IComponentManager; + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IComponentManager.cs b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs index b161b36..be1d1e2 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs @@ -1,135 +1,9 @@ -using System; -using System.Collections.Generic; -using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebEvent; -using WebExpress.WebCore.WebJob; -using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPackage; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; -using WebExpress.WebCore.WebSession; -using WebExpress.WebCore.WebSitemap; -using WebExpress.WebCore.WebStatusPage; -using WebExpress.WebCore.WebTask; - -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebComponent { /// - /// Interface of the central management of manager components. + /// Interface of the manager classes. /// - public interface IComponentManager : IManager + public interface IComponentManager { - /// - /// An event that fires when an component is added. - /// - event EventHandler AddComponent; - - /// - /// An event that fires when an component is removed. - /// - event EventHandler RemoveComponent; - - /// - /// Returns the reference to the context of the host. - /// - IHttpServerContext HttpServerContext { get; } - - /// - /// Returns all registered components. - /// - IEnumerable Managers { get; } - - /// - /// Returns the log manager. - /// - /// The instance of the log manager or null. - LogManager LogManager { get; } - - /// - /// Returns the package manager. - /// - /// The instance of the package manager or null. - PackageManager PackageManager { get; } - - /// - /// Returns the plugin manager. - /// - /// The instance of the plugin manager or null. - public IPluginManager PluginManager { get; } - /// - /// Returns the application manager. - /// - /// The instance of the application manager or null. - IApplicationManager ApplicationManager { get; } - - /// - /// Returns the module manager. - /// - /// The instance of the module manager or null. - IModuleManager ModuleManager { get; } - - /// - /// Returns the event manager. - /// - /// The instance of the event manager or null. - EventManager EventManager { get; } - - /// - /// Returns the job manager. - /// - /// The instance of the job manager or null. - JobManager JobManager { get; } - - /// - /// Returns the status page manager. - /// - /// The instance of the status page manager or null. - StatusPageManager StatusPageManager { get; } - - /// - /// Returns the resource manager. - /// - /// The instance of the resource manager or null. - IResourceManager ResourceManager { get; } - - /// - /// Returns the sitemap manager. - /// - /// The instance of the sitemap manager or null. - ISitemapManager SitemapManager { get; } - - /// - /// Returns the internationalization manager. - /// - /// The instance of the internationalization manager or null. - IInternationalizationManager InternationalizationManager { get; } - - /// - /// Returns the session manager. - /// - /// The instance of the session manager or null. - SessionManager SessionManager { get; } - - /// - /// Returns the task manager. - /// - /// The instance of the task manager manager or null. - TaskManager TaskManager { get; } - - /// - /// Returns a component based on its id. - /// - /// The id. - /// The instance of the component or null. - IManager GetComponent(string id); - - /// - /// Returns a component based on its type. - /// - /// The component class. - /// The instance of the component or null. - T GetComponent() where T : IManager; } } diff --git a/src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs b/src/WebExpress.WebCore/WebComponent/IComponentManagerPlugin.cs similarity index 94% rename from src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs rename to src/WebExpress.WebCore/WebComponent/IComponentManagerPlugin.cs index 796f974..9366404 100644 --- a/src/WebExpress.WebCore/WebComponent/IManagerPlugin.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentManagerPlugin.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebComponent /// /// Interface of the manager classes. /// - public interface IManagerPlugin : IManager + public interface IComponentManagerPlugin : IComponentManager { /// /// Discovers and registers entries from the specified plugin. diff --git a/src/WebExpress.WebCore/WebComponent/IEndpoint.cs b/src/WebExpress.WebCore/WebComponent/IEndpoint.cs new file mode 100644 index 0000000..8aa299f --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IEndpoint.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Defines the base contract for resources of all kinds, such as REST APIs or Pages. + /// + public interface IEndpoint : IComponent + { + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs b/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs new file mode 100644 index 0000000..1caf936 --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Represents the context of a endpoint. + /// + public interface IEndpointContext : IContext + { + /// + /// Returns the endpoint id. + /// + string EndpointId { get; } + + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding module context. + /// + IModuleContext ModuleContext { get; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + IEnumerable Scopes { get; } + + /// + /// Provides the conditions that must be met for the resource to be active. + /// + IEnumerable Conditions { get; } + + /// + /// Returns the parent or null if not used. + /// + IEndpointContext ParentContext { get; } + + /// + /// Determines whether the resource is created once and reused each time it is called. + /// + bool Cache { get; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + bool IncludeSubPaths { get; } + + /// + /// Returns the context path. + /// + UriResource ContextPath { get; } + + /// + /// Returns the uri. + /// + UriResource Uri { get; } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IManager.cs b/src/WebExpress.WebCore/WebComponent/IEndpointManager.cs similarity index 50% rename from src/WebExpress.WebCore/WebComponent/IManager.cs rename to src/WebExpress.WebCore/WebComponent/IEndpointManager.cs index 46e8e0d..2118e1e 100644 --- a/src/WebExpress.WebCore/WebComponent/IManager.cs +++ b/src/WebExpress.WebCore/WebComponent/IEndpointManager.cs @@ -1,9 +1,10 @@ namespace WebExpress.WebCore.WebComponent { /// - /// Interface of the manager classes. + /// Represents a endpoint manager. /// - public interface IManager + public interface IEndpointManager : IComponentManager { + } } diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 9867868..09a6363 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -11,9 +11,9 @@ namespace WebExpress.WebCore.WebEvent /// /// The event manager. /// - public sealed class EventManager : IManagerPlugin, ISystemComponent + public sealed class EventManager : IComponentManagerPlugin, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly EventDictionary _dictionary = []; @@ -22,7 +22,7 @@ public sealed class EventManager : IManagerPlugin, ISystemComponent /// /// The component manager. /// The reference to the context of the host. - internal EventManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + internal EventManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 41c32f6..20613f2 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; @@ -8,19 +7,9 @@ using System.Xml.Serialization; using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebEvent; -using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPackage; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; -using WebExpress.WebCore.WebSession; -using WebExpress.WebCore.WebSitemap; -using WebExpress.WebCore.WebStatusPage; -using WebExpress.WebCore.WebTask; using WebExpress.WebCore.WebUri; [assembly: InternalsVisibleTo("WebExpress.WebCore.Test")] @@ -32,7 +21,7 @@ namespace WebExpress.WebCore /// public class WebEx { - private static ComponentManager _componentManager; + private static ComponentHub _componentHub; private HttpServer _httpServer; /// @@ -46,97 +35,9 @@ public class WebEx public static string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(); /// - /// Returns the component manager. + /// Returns the component hub. /// - public static IComponentManager ComponentManager => _componentManager; - - /// - /// Returns the reference to the context of the host. - /// - public static IHttpServerContext HttpServerContext => _componentManager?.HttpServerContext; - - /// - /// Returns all registered components. - /// - public static IEnumerable Components => ComponentManager?.Managers; - - /// - /// Returns the log manager. - /// - /// The instance of the log manager or null. - public static LogManager LogManager => ComponentManager?.LogManager; - - /// - /// Returns the package manager. - /// - /// The instance of the package manager or null. - public static PackageManager PackageManager => ComponentManager?.PackageManager; - - /// - /// Returns the plugin manager. - /// - /// The instance of the plugin manager or null. - public static IPluginManager PluginManager => ComponentManager?.PluginManager; - - /// - /// Returns the application manager. - /// - /// The instance of the application manager or null. - public static IApplicationManager ApplicationManager => ComponentManager?.ApplicationManager; - - /// - /// Returns the module manager. - /// - /// The instance of the module manager or null. - public static IModuleManager ModuleManager => ComponentManager?.ModuleManager; - - /// - /// Returns the event manager. - /// - /// The instance of the event manager or null. - public static EventManager EventManager => ComponentManager?.EventManager; - - /// - /// Returns the job manager. - /// - /// The instance of the job manager or null. - public static JobManager JobManager => ComponentManager?.JobManager; - - /// - /// Returns the status page manager. - /// - /// The instance of the status page manager or null. - public static StatusPageManager StatusPageManager => ComponentManager?.StatusPageManager; - - /// - /// Returns the resource manager. - /// - /// The instance of the resource manager or null. - public static IResourceManager ResourceManager => ComponentManager?.ResourceManager; - - /// - /// Returns the sitemap manager. - /// - /// The instance of the sitemap manager or null. - public static ISitemapManager SitemapManager => ComponentManager?.SitemapManager; - - /// - /// Returns the internationalization manager. - /// - /// The instance of the internationalization manager or null. - public static IInternationalizationManager InternationalizationManager => ComponentManager?.InternationalizationManager; - - /// - /// Returns the session manager. - /// - /// The instance of the session manager or null. - public static SessionManager SessionManager => ComponentManager?.SessionManager; - - /// - /// Returns the task manager. - /// - /// The instance of the task manager manager or null. - public static TaskManager TaskManager => ComponentManager?.TaskManager; + public static IComponentHub ComponentHub => _componentHub; /// /// Entry point of application. @@ -233,7 +134,7 @@ public int Execution(string[] args) Initialization(ArgumentParser.Current.GetValidArguments(args), Path.Combine(Path.Combine(Environment.CurrentDirectory, "config"), argumentDict["config"])); // start the manager - _componentManager.Execute(); + _componentHub.Execute(); // starting the web server Start(); @@ -314,7 +215,7 @@ private void Initialization(string args, string configFile) Config = config }; - _componentManager = CreateComponentManager(_httpServer.HttpServerContext); + _componentHub = CreateComponentManager(_httpServer.HttpServerContext); // start logging _httpServer.HttpServerContext.Log.Begin(config.Log); @@ -383,7 +284,7 @@ private void Exit() _httpServer.HttpServerContext.Log.Seperator('/'); // Stop running - _componentManager.ShutDown(); + _componentHub.ShutDown(); // stop logging _httpServer.HttpServerContext.Log.Close(); @@ -394,9 +295,9 @@ private void Exit() /// /// The id. /// The instance of the component or null. - public static IManager GetComponent(string id) + public static IComponentManager GetComponent(string id) { - return _componentManager.GetComponent(id); + return _componentHub.GetComponent(id); } /// @@ -404,19 +305,19 @@ public static IManager GetComponent(string id) /// /// The component class. /// The instance of the component or null. - public static T GetComponent() where T : IManager + public static T GetComponent() where T : IComponentManager { - return _componentManager.GetComponent(); + return _componentHub.GetComponent(); } /// - /// Creates and returns a new instance of . + /// Creates and returns a new instance of . /// /// The HTTP server context used to initialize the component manager. - /// A new instance of . - protected virtual ComponentManager CreateComponentManager(IHttpServerContext httpServerContext) + /// A new instance of . + protected virtual ComponentHub CreateComponentManager(IHttpServerContext httpServerContext) { - return new ComponentManager(httpServerContext); + return new ComponentHub(httpServerContext); } } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 58d03c8..7b80adb 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -14,9 +14,9 @@ namespace WebExpress.WebCore.WebJob /// /// Processing of cyclic jobs. /// - public sealed class JobManager : IManagerPlugin, ISystemComponent, IExecutableElements + public sealed class JobManager : IComponentManagerPlugin, ISystemComponent, IExecutableElements { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly ScheduleDictionary _staticScheduleDictionary = []; private readonly List _dynamicScheduleList = []; @@ -28,7 +28,7 @@ public sealed class JobManager : IManagerPlugin, ISystemComponent, IExecutableEl /// /// The component manager. /// The reference to the context of the host. - internal JobManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + internal JobManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 34324c0..0c9855e 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebLog { - public class LogManager : IManagerPlugin, ISystemComponent + public class LogManager : IComponentManagerPlugin, ISystemComponent { /// /// An event that fires when an log is added. diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index 5bf43ed..c2dc6f3 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -449,7 +449,7 @@ private void ParseRequestParams() /// private void ParseSessionParams() { - Session = WebEx.ComponentManager.SessionManager?.GetSession(this); + Session = WebEx.ComponentHub.SessionManager?.GetSession(this); var property = Session?.GetProperty(); if (property != null && property.Params != null) diff --git a/src/WebExpress.WebCore/WebModule/IModuleContext.cs b/src/WebExpress.WebCore/WebModule/IModuleContext.cs index 654df92..7e4bda5 100644 --- a/src/WebExpress.WebCore/WebModule/IModuleContext.cs +++ b/src/WebExpress.WebCore/WebModule/IModuleContext.cs @@ -1,10 +1,11 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebModule { - public interface IModuleContext + public interface IModuleContext : IContext { /// /// Returns the context of the associated plugin. diff --git a/src/WebExpress.WebCore/WebModule/IModuleManager.cs b/src/WebExpress.WebCore/WebModule/IModuleManager.cs index 45bf179..13841f7 100644 --- a/src/WebExpress.WebCore/WebModule/IModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/IModuleManager.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebModule /// /// The interface of the module manager. /// - public interface IModuleManager : IManager + public interface IModuleManager : IComponentManager { /// /// An event that fires when an module is added. diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index 5607dc4..cce6a87 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -13,9 +13,9 @@ namespace WebExpress.WebCore.WebModule /// /// The module manager manages the WebExpress modules. /// - public sealed class ModuleManager : IModuleManager, IManagerPlugin, IExecutableElements, ISystemComponent + public sealed class ModuleManager : IModuleManager, IComponentManagerPlugin, IExecutableElements, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly ModuleDictionary _dictionary = []; @@ -42,7 +42,7 @@ public sealed class ModuleManager : IModuleManager, IManagerPlugin, IExecutableE /// /// The component manager. /// The reference to the context of the host. - private ModuleManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + private ModuleManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index b6e3133..c01a9dc 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -19,9 +19,9 @@ namespace WebExpress.WebCore.WebPackage /// /// The package manager manages packages with WebExpress extensions. The packages must be in WebExpressPackage format (*.wxp). /// - public sealed class PackageManager : IManager, ISystemComponent + public sealed class PackageManager : IComponentManager, ISystemComponent { - private readonly ComponentManager _componentManager; + private readonly ComponentHub _componentManager; private readonly PluginManager _pluginManager; /// @@ -55,9 +55,9 @@ public sealed class PackageManager : IManager, ISystemComponent /// The component manager. /// The plugin manager. /// The reference to the context of the host. - private PackageManager(IComponentManager componentManager, IPluginManager pluginManager, IHttpServerContext context) + private PackageManager(IComponentHub componentManager, IPluginManager pluginManager, IHttpServerContext context) { - _componentManager = componentManager as ComponentManager; + _componentManager = componentManager as ComponentHub; _pluginManager = pluginManager as PluginManager; HttpServerContext = context; diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index e98895c..7415ea3 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -1,18 +1,17 @@ -using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPage { - public interface IPage : IResource + /// + /// Defines the contract for a page resource. + /// + public interface IPage : IEndpoint { /// - /// Returns the resource context where the resource exists. + /// Processing of the page. /// - public IResourceContext ResourceContext { get; } - - /// - /// Returns or sets the page title. - /// - string Title { get; set; } + /// The context for rendering the page. + void Process(IRenderContext context); /// /// Redirect to another page. @@ -21,4 +20,12 @@ public interface IPage : IResource /// The uri to redirect to. void Redirecting(string uri); } + + /// + /// Defines the contract for a page resource that can be rendered using a specific context. + /// + /// The type of the render context. + public interface IPage : IPage where T : IRenderContext + { + } } diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs new file mode 100644 index 0000000..3de4921 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -0,0 +1,15 @@ +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// Defines the context for a page, providing access to various related contexts and properties. + /// + public interface IPageContext : IEndpointContext + { + /// + /// Returns the resource title. + /// + string PageTitle { get; } + } +} diff --git a/src/WebExpress.WebCore/WebPage/IPageManager.cs b/src/WebExpress.WebCore/WebPage/IPageManager.cs new file mode 100644 index 0000000..a573e67 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/IPageManager.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// The page manager manages page elements, which can be called with a URI (Uniform Resource Identifier). + /// + public interface IPageManager : IEndpointManager + { + /// + /// An event that fires when an page is added. + /// + event EventHandler AddPage; + + /// + /// An event that fires when an page is removed. + /// + event EventHandler RemovePage; + + /// + /// Returns all pages contexts. + /// + IEnumerable Pages { get; } + + /// + /// Returns an enumeration of all containing page contexts of a plugin. + /// + /// A context of a plugin whose pages are to be registered. + /// An enumeration of page contexts. + IEnumerable GetPages(IPluginContext pluginContext); + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// An enumeration of page contextes. + IEnumerable GetPages() where T : IPage; + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// An enumeration of page contextes. + IEnumerable GetPages(Type pageType); + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the module. + /// An enumeration of page contextes. + IEnumerable GetPages(Type pageType, IModuleContext moduleContext); + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the module. + /// An enumeration of page contextes. + IEnumerable GetPages(IModuleContext moduleContext) where T : IPage; + + /// + /// Returns the page context. + /// + /// The context of the module. + /// The page id. + /// An page context or null. + IPageContext GetPage(IModuleContext moduleContext, string pageId); + + /// + /// Returns the page context. + /// + /// The context of the module. + /// The page type. + /// An page context or null. + IPageContext GetPage(IModuleContext moduleContext, Type pageType); + + /// + /// Returns the page context. + /// + /// The application id. + /// The module id. + /// The page id. + /// An page context or null. + IPageContext GetPage(string applicationId, string moduleId, string pageId); + } +} diff --git a/src/WebExpress.WebCore/WebPage/IRenderContext.cs b/src/WebExpress.WebCore/WebPage/IRenderContext.cs new file mode 100644 index 0000000..e69b07d --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/IRenderContext.cs @@ -0,0 +1,42 @@ +using System.Globalization; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// Represents the interface of the context in which rendering occurs, providing access to the page, request, culture, and visual tree. + /// + public interface IRenderContext + { + /// + /// Returns the page where is rendered. + /// + IPage Page { get; } + + /// + /// Returns the request. + /// + Request Request { get; } + + /// + /// The uri of the request. + /// + UriResource Uri { get; } + + /// + /// Returns the culture. + /// + CultureInfo Culture { get; } + + /// + /// Provides the context of the associated resource. + /// + IPageContext PageContext { get; } + + /// + /// Returns the contents of a page. + /// + IVisualTree VisualTree { get; } + } +} diff --git a/src/WebExpress.WebCore/WebPage/IVisualTree.cs b/src/WebExpress.WebCore/WebPage/IVisualTree.cs index 6bafcac..e7f3181 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTree.cs @@ -67,8 +67,8 @@ public interface IVisualTree /// /// Convert to html. /// - /// The context for rendering the page. + /// The context for rendering the visual tree. /// The page as html. - IHtmlNode Render(RenderContext context); + IHtmlNode Render(IVisualTreeContext context); } } diff --git a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs new file mode 100644 index 0000000..66b7c20 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs @@ -0,0 +1,37 @@ +using System.Globalization; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// Represents the context of a visual tree. + /// + public interface IVisualTreeContext + { + /// + /// Returns the page where is rendered. + /// + IPage Page { get; } + + /// + /// Returns the request. + /// + Request Request { get; } + + /// + /// The uri of the request. + /// + UriResource Uri { get; } + + /// + /// Returns the culture. + /// + CultureInfo Culture { get; } + + /// + /// Provides the context of the associated page. + /// + IPageContext PageContext { get; } + } +} diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs new file mode 100644 index 0000000..e0bd823 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebPage.Model +{ + /// + /// key = plugin context + /// value = { key = resource id, value = ressource item } + /// + internal class PageDictionary : Dictionary> + { + } +} diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs new file mode 100644 index 0000000..7afb0d1 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage.Model +{ + /// + /// A page element that contains meta information about a page. + /// + internal class PageItem : IDisposable + { + private readonly IPageManager _pageManager; + + /// + /// An event that fires when an page is added. + /// + public event EventHandler AddPage; + + /// + /// An event that fires when an page is removed. + /// + public event EventHandler RemovePage; + + /// + /// Returns or sets the page id. + /// + public string PageId { get; set; } + + /// + /// Returns or sets the resource title. + /// + public string Title { get; set; } + + /// + /// Returns or sets the parent id. + /// + public string ParentId { get; set; } + + /// + /// Returns or sets the type of page. + /// + public Type PageClass { get; set; } + + /// + /// Returns or sets the instance of the page, if the page is cached, otherwise null. + /// + public IPage Instance { get; set; } + + /// + /// Returns or sets the module id. + /// + public string ModuleId { get; set; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + public IReadOnlyList Scopes { get; set; } + + /// + /// Returns or sets the paths of the resource. + /// + public UriResource ContextPath { get; set; } + + /// + /// Returns or sets the path segment. + /// + public IUriPathSegment PathSegment { get; internal set; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; set; } + + /// + /// Returns the conditions that must be met for the resource to be active. + /// + public ICollection Conditions { get; set; } + + /// + /// Returns whether the resource is created once and reused each time it is called. + /// + public bool Cache { get; set; } + + /// + /// Returns whether it is a optional resource. + /// + public bool Optional { get; set; } + + /// + /// Returns the log to write status messages to the console and to a log file. + /// + public ILog Log { get; internal set; } + + /// + /// Returns the directory where the module instances are listed. + /// + private IDictionary Dictionary { get; } + = new Dictionary(); + + /// + /// Returns the associated module contexts. + /// + public IEnumerable ModuleContexts => Dictionary.Keys; + + /// + /// Returns the page contexts. + /// + public IEnumerable PageContexts => Dictionary.Values; + + /// + /// Initializes a new instance of the class. + /// + /// The page manager. + internal PageItem(IPageManager pageManager) + { + _pageManager = pageManager; + } + + /// + /// Adds an module assignment + /// + /// The context of the module. + public void AddModule(IModuleContext moduleContext) + { + // only if no instance has been created yet + if (Dictionary.ContainsKey(moduleContext)) + { + Log.Warning(message: I18N.Translate("webexpress:pagemanager.addresource.duplicate", PageId, moduleContext.ModuleId)); + + return; + } + + // create context + var pageContext = new PageContext(moduleContext, _pageManager, this) + { + Scopes = Scopes, + Conditions = Conditions, + EndpointId = PageId, + PageTitle = Title, + Cache = Cache, + IncludeSubPaths = IncludeSubPaths + }; + + if + ( + !Optional || + moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.{PageId.ToLower()}") || + moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.*") || + moduleContext.ApplicationContext.Options.Where(x => Regex.Match($"{ModuleId.ToLower()}.{PageId.ToLower()}", x).Success).Any() + ) + { + Dictionary.Add(moduleContext, pageContext); + OnAddPage(pageContext); + } + } + + /// + /// Remove an module assignment + /// + /// The context of the module. + public void DetachModule(IModuleContext moduleContext) + { + // not an assignment has been created yet + if (!Dictionary.ContainsKey(moduleContext)) + { + return; + } + + foreach (var resourceContext in Dictionary.Values) + { + OnRemovePage(resourceContext); + } + + Dictionary.Remove(moduleContext); + } + + /// + /// Checks whether a module context is already assigned to the item. + /// + /// The module context. + /// True a mapping exists, false otherwise. + public bool IsAssociatedWithModule(IModuleContext moduleContext) + { + return Dictionary.ContainsKey(moduleContext); + } + + /// + /// Raises the AddPage event. + /// + /// The page context. + private void OnAddPage(IPageContext pageContext) + { + AddPage?.Invoke(this, pageContext); + } + + /// + /// Raises the RemovePage event. + /// + /// The page context. + private void OnRemovePage(IPageContext pageContext) + { + RemovePage?.Invoke(this, pageContext); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + foreach (Delegate d in AddPage.GetInvocationList()) + { + AddPage -= (EventHandler)d; + } + + foreach (Delegate d in RemovePage.GetInvocationList()) + { + RemovePage -= (EventHandler)d; + } + } + + /// + /// Convert the resource element to a string. + /// + /// The resource element in its string representation. + public override string ToString() + { + return $"Page '{PageId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index 9082b40..bb0b567 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebResource; namespace WebExpress.WebCore.WebPage { @@ -17,20 +16,12 @@ namespace WebExpress.WebCore.WebPage /// /// Initializes a new instance of the class. /// - public Page() + /// The context of the page. + public Page(IPageContext pageContext) { } - /// - /// Initialization - /// - /// The context of the resource. - public override void Initialization(IResourceContext context) - { - base.Initialization(context); - } - /// /// Redirect to another page. /// The function throws the RedirectException. @@ -42,30 +33,9 @@ public void Redirecting(string uri) } /// - /// Processing of the resource. - /// - /// The request. - /// The response. - public override Response Process(Request request) - { - var context = new T() - { - Page = this, - Request = request - }; - - Process(context); - - return new ResponseOK() - { - Content = context.VisualTree.Render(context) - }; - } - - /// - /// Processing of the resource. + /// Processing of the page. /// /// The context for rendering the page. - public abstract void Process(T context); + public abstract void Process(IRenderContext context); } } diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs new file mode 100644 index 0000000..c181bcc --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPage.Model; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// Represents the context of a page. + /// + public class PageContext : IPageContext + { + private readonly IPageManager _pageManager; + private readonly PageItem _pageItem; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Returns the corresponding module context. + /// + public IModuleContext ModuleContext { get; private set; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + public IEnumerable Scopes { get; internal set; } + + /// + /// Returns the conditions that must be met for the resource to be active. + /// + public IEnumerable Conditions { get; internal set; } = new List(); + + /// + /// Returns the endpoint id. + /// + public string EndpointId { get; internal set; } + + /// + /// Returns the resource title. + /// + public string PageTitle { get; internal set; } + + /// + /// Returns the parent or null if not used. + /// + public IEndpointContext ParentContext => _pageManager.Pages + .Where(x => !string.IsNullOrWhiteSpace(_pageItem.ParentId)) + .Where(x => x.EndpointId.Equals(_pageItem.ParentId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) + .FirstOrDefault(); + + /// + /// Returns whether the resource is created once and reused each time it is called. + /// + public bool Cache { get; internal set; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; internal set; } + + /// + /// Returns the context path. + /// + public UriResource ContextPath + { + get + { + var parentContext = ParentContext; + if (parentContext != null) + { + return UriResource.Combine(ParentContext?.Uri, _pageItem.ContextPath); + } + + return UriResource.Combine(ModuleContext.ContextPath, _pageItem.ContextPath); + } + } + + /// + /// Returns the uri. + /// + public UriResource Uri => ContextPath.Append(_pageItem.PathSegment); + + /// + /// Initializes a new instance of the class. + /// + /// The module context. + /// The resource manager. + /// The page item or null. + internal PageContext(IModuleContext moduleContext, IPageManager pageManager, PageItem pageItem = null) + { + PluginContext = moduleContext?.PluginContext; + ModuleContext = moduleContext; + _pageManager = pageManager; + _pageItem = pageItem; + } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs new file mode 100644 index 0000000..3506860 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -0,0 +1,541 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPage.Model; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebScope; +using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebStatusPage; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// The page manager manages page elements, which can be called with a URI (Uniform page Identifier). + /// + public class PageManager : IPageManager + { + private readonly IComponentHub _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly PageDictionary _dictionary = []; + + /// + /// An event that fires when an page is added. + /// + public event EventHandler AddPage; + + /// + /// An event that fires when an page is removed. + /// + public event EventHandler RemovePage; + + /// + /// Returns all page contexts. + /// + public IEnumerable Pages => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts); + + /// + /// Initializes a new instance of the class. + /// + /// The component manager. + /// The reference to the context of the host. + private PageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + { + _componentManager = componentManager; + + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + { + Register(pluginContext); + }; + + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + { + Remove(pluginContext); + }; + + _componentManager.ModuleManager.AddModule += (sender, moduleContext) => + { + AssignToModule(moduleContext); + }; + + _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => + { + DetachFromModule(moduleContext); + }; + + _componentManager.SitemapManager.Register + ( + new EndpointRegistration() + { + Factory = (resourceContext, uri, culture) => CreatePageInstance(resourceContext as IPageContext, culture), + ContextResolver = (type, moduleContext) => moduleContext != null ? GetPages(type, moduleContext) : GetPages(type), + EndpointResolver = () => Pages, + HandleRequest = (endpoint, endpontContext, request) => + { + var typeOfT = endpoint.GetType().GetGenericArguments()[0]; + object[] parameters = [endpoint, endpontContext as IPageContext, request]; + + var context = (IRenderContext)Activator.CreateInstance(typeOfT, parameters); + + (endpoint as IPage).Process(context); + + return new ResponseOK() + { + Content = context.VisualTree.Render(new VisualTreeContext(context)) + }; + } + } + ); + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress:pagemanager.initialization") + ); + } + + /// + /// Returns an enumeration of all containing page contexts of a plugin. + /// + /// A context of a plugin whose pages are to be registered. + /// An enumeration of page contexts. + public IEnumerable GetPages(IPluginContext pluginContext) + { + if (!_dictionary.ContainsKey(pluginContext)) + { + return []; + } + + return _dictionary[pluginContext].Values + .SelectMany(x => x.PageContexts); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// An enumeration of page contextes. + public IEnumerable GetPages() where T : IPage + { + return GetPages(typeof(T)); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// An enumeration of page contextes. + public IEnumerable GetPages(Type pageType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .Where(x => x.PageClass.Equals(pageType)) + .Select(x => x.PageContext); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the module. + /// An enumeration of page contextes. + public IEnumerable GetPages(Type pageType, IModuleContext moduleContext) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageClass.Equals(pageType)) + .Select(x => x.PageContext); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the module. + /// An enumeration of page contextes. + public IEnumerable GetPages(IModuleContext moduleContext) where T : IPage + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageClass.Equals(typeof(T))) + .Select(x => x.PageContext); + } + + /// + /// Returns the page context. + /// + /// The context of the module. + /// The page id. + /// An page context or null. + public IPageContext GetPage(IModuleContext moduleContext, string pageId) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .Where(x => x.PageContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.PageContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageContext.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.PageContext) + .FirstOrDefault(); + } + + /// + /// Returns the page context. + /// + /// The context of the module. + /// The page type. + /// An page context or null. + public IPageContext GetPage(IModuleContext moduleContext, Type pageType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .Where(x => x.PageContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.PageContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.PageClass.Equals(pageType)) + .Select(x => x.PageContext) + .FirstOrDefault(); + } + + /// + /// Returns the page context. + /// + /// The application id. + /// The module id. + /// The page id. + /// An page context or null. + public IPageContext GetPage(string applicationId, string moduleId, string pageId) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.PageContexts) + .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) + .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + } + + /// + /// Creates a new page and returns it. If a page already exists (through caching), the existing instance is returned. + /// + /// The context used for page creation. + /// The culture with the language settings. + /// The created or cached page. + private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) + { + var resourceItem = _dictionary.Values + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.PageContexts.Contains(pageContext)); + + if (resourceItem != null && resourceItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _componentManager); + + if (instance is II18N i18n) + { + i18n.Culture = culture; + } + + if (resourceItem.Cache) + { + resourceItem.Instance = instance; + } + + return instance; + } + + return resourceItem?.Instance as IPage; + } + + /// + /// Discovers and registers pages from the specified plugin. + /// + /// A context of a plugin whose pages are to be registered. + public void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + var assembly = pluginContext?.Assembly; + + _dictionary.Add(pluginContext, []); + var dict = _dictionary[pluginContext]; + + foreach (var resourceType in assembly.GetTypes() + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(IPage).Name) != null) + .Where(x => x.GetInterface(typeof(IStatusPage).Name) == null)) + { + var id = resourceType.FullName?.ToLower(); + var segment = default(ISegmentAttribute); + var title = resourceType.Name; + var parent = default(string); + var contextPath = string.Empty; + var includeSubPaths = false; + var moduleId = string.Empty; + var scopes = new List(); + var conditions = new List(); + var optional = false; + var cache = false; + + foreach (var customAttribute in resourceType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IResourceAttribute)))) + { + var buf = typeof(ModuleAttribute<>); + + if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) + { + segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; + } + else if (customAttribute.AttributeType == typeof(TitleAttribute)) + { + title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) + { + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + } + else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) + { + contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(IncludeSubPathsAttribute)) + { + includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); + } + else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) + { + moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + } + else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) + { + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + } + else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) + { + var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + conditions.Add(Activator.CreateInstance(condition) as ICondition); + } + else if (customAttribute.AttributeType == typeof(CacheAttribute)) + { + cache = true; + } + else if (customAttribute.AttributeType == typeof(OptionalAttribute)) + { + optional = true; + } + } + + if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) + { + scopes.Add(resourceType.FullName?.ToLower()); + } + + if (string.IsNullOrEmpty(moduleId)) + { + // no module specified + _httpServerContext.Log.Warning + ( + I18N.Translate + ( + "webexpress:pagemanager.moduleless", + id + ) + ); + + continue; + } + + if (!dict.ContainsKey(id)) + { + var pageItem = new PageItem(_componentManager.PageManager) + { + PageId = id, + Title = title, + ParentId = parent, + PageClass = resourceType, + ModuleId = moduleId, + Scopes = scopes, + Cache = cache, + Conditions = conditions, + ContextPath = new UriResource(contextPath), + IncludeSubPaths = includeSubPaths, + PathSegment = segment.ToPathSegment(), + Optional = optional, + Log = _httpServerContext?.Log + }; + + pageItem.AddPage += (s, e) => + { + OnAddPage(e); + }; + + pageItem.RemovePage += (s, e) => + { + OnRemovePage(e); + }; + + dict.Add(id, pageItem); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress:pagemanager.addresource", + id, + moduleId + ) + ); + } + + // assign the resource to existing modules. + foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) + { + AssignToModule(moduleContext); + } + } + } + + /// + /// Discovers and registers resources from the specified plugin. + /// + /// A list with plugin contexts that contain the resources. + public void Register(IEnumerable pluginContexts) + { + foreach (var pluginContext in pluginContexts) + { + Register(pluginContext); + } + } + + /// + /// Removes all pages associated with the specified plugin context. + /// + /// The context of the plugin that contains the pages to remove. + public void Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return; + } + + // the plugin has not been registered in the manager + if (!_dictionary.ContainsKey(pluginContext)) + { + return; + } + + foreach (var resourceItem in _dictionary[pluginContext].Values) + { + resourceItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + } + + /// + /// Assign existing resources to the module. + /// + /// The context of the module. + private void AssignToModule(IModuleContext moduleContext) + { + foreach (var resourceItem in _dictionary.Values + .SelectMany(x => x.Values) + .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => !x.IsAssociatedWithModule(moduleContext))) + { + resourceItem.AddModule(moduleContext); + } + } + + /// + /// Remove an existing modules to the application. + /// + /// The context of the module. + private void DetachFromModule(IModuleContext moduleContext) + { + foreach (var resourceItem in _dictionary.Values + .SelectMany(x => x.Values) + .Where(x => !x.IsAssociatedWithModule(moduleContext))) + { + resourceItem.DetachModule(moduleContext); + } + } + + /// + /// Returns an enumeration of all containing page items of a plugin. + /// + /// A context of a plugin whose pages are to be registered. + /// An enumeration of pages items. + private IEnumerable GetPageItems(IPluginContext pluginContext) + { + if (!_dictionary.ContainsKey(pluginContext)) + { + return []; + } + + return _dictionary[pluginContext].Values; + } + + /// + /// Raises the AddPage event. + /// + /// The page context. + private void OnAddPage(IPageContext resourceContext) + { + AddPage?.Invoke(this, resourceContext); + } + + /// + /// Raises the RemovePage event. + /// + /// The page context. + private void OnRemovePage(IPageContext pageContext) + { + RemovePage?.Invoke(this, pageContext); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + /// The context of the plugin. + /// A list of log entries. + /// The shaft deep. + public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + { + foreach (var resourcenItem in GetPageItems(pluginContext)) + { + output.Add + ( + string.Empty.PadRight(deep) + + I18N.Translate + ( + "webexpress:pagemanager.resource", + resourcenItem.PageId, + string.Join(",", resourcenItem.ModuleId) + ) + ); + } + } + } +} diff --git a/src/WebExpress.WebCore/WebResource/RedirectException.cs b/src/WebExpress.WebCore/WebPage/RedirectException.cs similarity index 95% rename from src/WebExpress.WebCore/WebResource/RedirectException.cs rename to src/WebExpress.WebCore/WebPage/RedirectException.cs index fe25760..9d189f4 100644 --- a/src/WebExpress.WebCore/WebResource/RedirectException.cs +++ b/src/WebExpress.WebCore/WebPage/RedirectException.cs @@ -1,6 +1,6 @@ using System; -namespace WebExpress.WebCore.WebResource +namespace WebExpress.WebCore.WebPage { public class RedirectException : Exception { diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 329476a..04daddf 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,22 +1,23 @@ using System.Globalization; -using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage { - public class RenderContext + /// + /// Represents the context in which rendering occurs, providing access to the page, request, culture, and visual tree. + /// + public class RenderContext : IRenderContext { /// /// Returns the page where is rendered. /// - public IPage Page { get; internal set; } + public IPage Page { get; protected set; } /// /// Returns the request. /// - public Request Request { get; internal set; } + public Request Request { get; protected set; } /// /// The uri of the request. @@ -29,14 +30,9 @@ public class RenderContext public CultureInfo Culture => Request?.Culture; /// - /// Provides the context of the associated plugin. + /// Provides the context of the associated page. /// - public IPluginContext PluginContext => Page?.ResourceContext?.PluginContext; - - /// - /// Provides the context of the associated application. - /// - public IApplicationContext ApplicationContext => Page?.ResourceContext?.ModuleContext?.ApplicationContext; + public IPageContext PageContext { get; protected set; } /// /// Returns the contents of a page. @@ -46,21 +42,15 @@ public class RenderContext /// /// Initializes a new instance of the class. /// - public RenderContext() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The page where is rendered. - /// The request. - /// The visual tree. - public RenderContext(IPage page, Request request, IVisualTree visualTree) + /// The page where the rendering is taking place. + /// The context of the associated page. + /// The request associated with the rendering context. + public RenderContext(IPage page, IPageContext pageContext, Request request) { Page = page; + PageContext = pageContext; Request = request; - VisualTree = visualTree; + VisualTree = CreateVisualTree(); } /// @@ -68,8 +58,17 @@ public RenderContext(IPage page, Request request, IVisualTree visualTree) /// /// The context to copy./param> public RenderContext(RenderContext context) - : this(context?.Page, context?.Request, context?.VisualTree) + : this(context?.Page, context.PageContext, context?.Request) + { + } + + /// + /// Creates the visual tree representing the contents of a page. + /// + /// A new instance of the interface. + protected virtual IVisualTree CreateVisualTree() { + return new VisualTree(); } } } diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index 8eb72d9..319031e 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -105,12 +105,12 @@ public virtual void AddHeaderScriptLinks(string url) /// /// Convert to html. /// - /// The context for rendering the page. + /// The context for rendering the visual tree. /// The page as an html tree. - public virtual IHtmlNode Render(RenderContext context) + public virtual IHtmlNode Render(IVisualTreeContext context) { var html = new HtmlElementRootHtml(); - html.Head.Title = I18N.Translate(context.Request, context.Page?.Title); + html.Head.Title = I18N.Translate(context.Request, context.PageContext.PageTitle); html.Head.Favicons = Favicons?.Select(x => new Favicon(x.Url, x.Mediatype)); //html.Head.Base = Context.ContextPath.ToString(); html.Head.Styles = Styles; diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs new file mode 100644 index 0000000..1d57699 --- /dev/null +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -0,0 +1,59 @@ +using System.Globalization; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebPage +{ + /// + /// Represents the context of a visual tree. + /// + public class VisualTreeContext : IVisualTreeContext + { + /// + /// Returns the page where is rendered. + /// + public IPage Page { get; protected set; } + + /// + /// Returns the request. + /// + public Request Request { get; protected set; } + + /// + /// The uri of the request. + /// + public UriResource Uri => Request?.Uri; + + /// + /// Returns the culture. + /// + public CultureInfo Culture => Request?.Culture; + + /// + /// Provides the context of the associated page. + /// + public IPageContext PageContext { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The page where the rendering is taking place. + /// The context of the associated resource. + /// The request associated with the rendering context. + public VisualTreeContext(IPage page, IPageContext resourceContext, Request request) + { + Page = page; + PageContext = resourceContext; + Request = request; + } + + /// + /// Initializes a new instance of the class. + /// + /// The context to copy./param> + public VisualTreeContext(IRenderContext context) + : this(context?.Page, context.PageContext, context?.Request) + { + } + } +} diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs index 9ecc646..bf8ac07 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebPlugin /// /// The plugin manager manages the WebExpress plugins. /// - public interface IPluginManager : IManager + public interface IPluginManager : IComponentManager { /// /// An event that fires when an plugin is added. diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index ca878b3..2093821 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -17,7 +17,7 @@ namespace WebExpress.WebCore.WebPlugin /// public sealed class PluginManager : IPluginManager, IExecutableElements, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly PluginDictionary _dictionary = []; private readonly PluginDictionary _unfulfilledDependencies = []; @@ -42,7 +42,7 @@ public sealed class PluginManager : IPluginManager, IExecutableElements, ISystem /// /// The component manager. /// The reference to the context of the host. - private PluginManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + private PluginManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; diff --git a/src/WebExpress.WebCore/WebResource/IResource.cs b/src/WebExpress.WebCore/WebResource/IResource.cs index 747a92d..32e9a1d 100644 --- a/src/WebExpress.WebCore/WebResource/IResource.cs +++ b/src/WebExpress.WebCore/WebResource/IResource.cs @@ -3,27 +3,16 @@ namespace WebExpress.WebCore.WebResource { - public interface IResource : IComponent + /// + /// Defines the contract for a resource component. + /// + public interface IResource : IEndpoint { - /// - /// Preprocessing of the resource. - /// - /// The request. - void PreProcess(Request request); - /// /// Processing of the resource. /// /// The request. /// The response. Response Process(Request request); - - /// - /// Post-processing of the resource. - /// - /// The request. - /// The response. - /// The response. - Response PostProcess(Request request, Response response); } } diff --git a/src/WebExpress.WebCore/WebResource/IResourceContext.cs b/src/WebExpress.WebCore/WebResource/IResourceContext.cs index dc97be1..dde1df0 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceContext.cs @@ -1,69 +1,12 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebResource { - public interface IResourceContext : IContext + /// + /// Defines the context for a resource, providing access to various related contexts and properties. + /// + public interface IResourceContext : IEndpointContext { - /// - /// Returns the associated plugin context. - /// - IPluginContext PluginContext { get; } - /// - /// Returns the corresponding module context. - /// - IModuleContext ModuleContext { get; } - - /// - /// Returns the scope names that provides the resource. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - IEnumerable Scopes { get; } - - /// - /// Provides the conditions that must be met for the resource to be active. - /// - IEnumerable Conditions { get; } - - /// - /// Returns the resource id. - /// - string ResourceId { get; } - - /// - /// Returns the resource title. - /// - string ResourceTitle { get; } - - /// - /// Returns the parent or null if not used. - /// - IResourceContext ParentContext { get; } - - /// - /// Determines whether the resource is created once and reused each time it is called. - /// - bool Cache { get; } - - /// - /// Returns or sets whether all subpaths should be taken into sitemap. - /// - bool IncludeSubPaths { get; } - - /// - /// Returns the context path. - /// - UriResource ContextPath { get; } - - /// - /// Returns the uri. - /// - UriResource Uri { get; } } } diff --git a/src/WebExpress.WebCore/WebResource/IResourceManager.cs b/src/WebExpress.WebCore/WebResource/IResourceManager.cs index ca2a8a8..5a021e3 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; @@ -8,9 +7,9 @@ namespace WebExpress.WebCore.WebResource { /// - /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). + /// The resource manager manages resources elements, which can be called with a URI (Uniform Resource Identifier). /// - public interface IResourceManager : IManager + public interface IResourceManager : IEndpointManager { /// /// An event that fires when an resource is added. @@ -48,6 +47,14 @@ public interface IResourceManager : IManager /// An enumeration of resource contextes. IEnumerable GetResorces(Type resourceType); + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// The context of the module. + /// An enumeration of resource contextes. + IEnumerable GetResorces(Type resourceType, IModuleContext moduleContext); + /// /// Returns an enumeration of resource contextes. /// @@ -80,13 +87,5 @@ public interface IResourceManager : IManager /// The resource id. /// An resource context or null. IResourceContext GetResorce(string applicationId, string moduleId, string resourceId); - - /// - /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. - /// - /// The context used for resource creation. - /// The culture with the language settings. - /// The created or cached resource. - IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture); } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceDictionary.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs similarity index 87% rename from src/WebExpress.WebCore/WebResource/ResourceDictionary.cs rename to src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs index fb68497..7e5ef5f 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceDictionary.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebResource +namespace WebExpress.WebCore.WebResource.Model { /// /// key = plugin context diff --git a/src/WebExpress.WebCore/WebResource/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs similarity index 97% rename from src/WebExpress.WebCore/WebResource/ResourceItem.cs rename to src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index 44f04e6..f045d05 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -3,12 +3,13 @@ using System.Linq; using System.Text.RegularExpressions; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebResource +namespace WebExpress.WebCore.WebResource.Model { /// /// A resource element that contains meta information about a resource. @@ -50,7 +51,7 @@ internal class ResourceItem : IDisposable /// /// Returns or sets the instance of the resource, if the resource is cached, otherwise null. /// - public IResource Instance { get; set; } + public IEndpoint Instance { get; set; } /// /// Returns or sets the module id. @@ -143,8 +144,7 @@ public void AddModule(IModuleContext moduleContext) { Scopes = Scopes, Conditions = Conditions, - ResourceId = ResourceId, - ResourceTitle = Title, + EndpointId = ResourceId, Cache = Cache, IncludeSubPaths = IncludeSubPaths }; diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index d03b3b0..b686657 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -25,19 +25,10 @@ public class ResourceAsset : ResourceBinary /// /// Initializes a new instance of the class. /// - public ResourceAsset() + /// The resource context. + public ResourceAsset(IResourceContext resourceContext) { Gard = new object(); - } - - /// - /// Initialization - /// - /// The context. - public override void Initialization(IResourceContext context) - { - base.Initialization(context); - AssetDirectory = ResourceContext.PluginContext.Assembly.GetName().Name; } diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index 0edf477..4570e60 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource @@ -41,19 +43,14 @@ public class ResourceContext : IResourceContext /// /// Returns the resource id. /// - public string ResourceId { get; internal set; } - - /// - /// Returns the resource title. - /// - public string ResourceTitle { get; internal set; } + public string EndpointId { get; internal set; } /// /// Returns the parent or null if not used. /// - public IResourceContext ParentContext => _resourceManager.Resources + public IEndpointContext ParentContext => _resourceManager.Resources .Where(x => !string.IsNullOrWhiteSpace(_resourceItem.ParentId)) - .Where(x => x.ResourceId.Equals(_resourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.EndpointId.Equals(_resourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) .FirstOrDefault(); @@ -109,7 +106,7 @@ internal ResourceContext(IModuleContext moduleContext, IResourceManager resource /// A string that represents the current object. public override string ToString() { - return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{ResourceId}"; + return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 155ae0f..487b3c0 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -8,7 +8,9 @@ using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource.Model; using WebExpress.WebCore.WebScope; +using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; @@ -17,9 +19,9 @@ namespace WebExpress.WebCore.WebResource /// /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). /// - public sealed class ResourceManager : IResourceManager, IManagerPlugin, ISystemComponent + public sealed class ResourceManager : IResourceManager, IComponentManagerPlugin, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly ResourceDictionary _dictionary = []; @@ -33,11 +35,6 @@ public sealed class ResourceManager : IResourceManager, IManagerPlugin, ISystemC /// public event EventHandler RemoveResource; - /// - /// Returns all resource items. - /// - internal IEnumerable ResourceItems => _dictionary.Values.SelectMany(x => x.Values); - /// /// Returns all resource contexts. /// @@ -50,7 +47,7 @@ public sealed class ResourceManager : IResourceManager, IManagerPlugin, ISystemC /// /// The component manager. /// The reference to the context of the host. - internal ResourceManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + private ResourceManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; @@ -74,6 +71,17 @@ internal ResourceManager(IComponentManager componentManager, IHttpServerContext DetachFromModule(moduleContext); }; + _componentManager.SitemapManager.Register + ( + new EndpointRegistration() + { + Factory = (resourceContext, uri, culture) => CreateResourceInstance(resourceContext as IResourceContext, culture), + ContextResolver = (type, moduleContext) => moduleContext != null ? GetResorces(type, moduleContext) : GetResorces(type), + EndpointResolver = () => Resources, + HandleRequest = (endpoint, endpointContext, request) => { return (endpoint as IResource).Process(request); } + } + ); + _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -95,7 +103,7 @@ public void Register(IPluginContext pluginContext) var assembly = pluginContext?.Assembly; - _dictionary.Add(pluginContext, new Dictionary()); + _dictionary.Add(pluginContext, []); var dict = _dictionary[pluginContext]; foreach (var resourceType in assembly.GetTypes() @@ -245,6 +253,31 @@ public void Register(IEnumerable pluginContexts) } } + /// + /// Removes all resources associated with the specified plugin context. + /// + /// The context of the plugin that contains the resources to remove. + public void Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return; + } + + // the plugin has not been registered in the manager + if (!_dictionary.ContainsKey(pluginContext)) + { + return; + } + + foreach (var resourceItem in _dictionary[pluginContext].Values) + { + resourceItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + } + /// /// Assign existing resources to the module. /// @@ -279,7 +312,7 @@ private void DetachFromModule(IModuleContext moduleContext) /// /// A context of a plugin whose resources are to be registered. /// An enumeration of resource items. - internal IEnumerable GetResorceItems(IPluginContext pluginContext) + private IEnumerable GetResorceItems(IPluginContext pluginContext) { if (!_dictionary.ContainsKey(pluginContext)) { @@ -298,7 +331,7 @@ public IEnumerable GetResorces(IPluginContext pluginContext) { if (!_dictionary.ContainsKey(pluginContext)) { - return new List(); + return []; } return _dictionary[pluginContext].Values @@ -329,6 +362,22 @@ public IEnumerable GetResorces(Type resourceType) .Select(x => x.ResourceContext); } + /// + /// Returns an enumeration of resource contextes. + /// + /// The resource type. + /// The context of the module. + /// An enumeration of resource contextes. + public IEnumerable GetResorces(Type resourceType, IModuleContext moduleContext) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceClass.Equals(resourceType)) + .Select(x => x.ResourceContext); + } + /// /// Returns an enumeration of resource contextes. /// @@ -378,7 +427,7 @@ public IResourceContext GetResorce(IModuleContext moduleContext, string resource .Where(x => x.ResourceContext.ModuleContext?.ApplicationContext != null) .Where(x => x.ResourceContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceContext.ResourceId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ResourceContext.EndpointId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) .Select(x => x.ResourceContext) .FirstOrDefault(); } @@ -398,7 +447,7 @@ public IResourceContext GetResorce(string applicationId, string moduleId, string .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.EndpointId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); } @@ -408,7 +457,7 @@ public IResourceContext GetResorce(string applicationId, string moduleId, string /// The context used for resource creation. /// The culture with the language settings. /// The created or cached resource. - public IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture) + private IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) @@ -431,32 +480,7 @@ public IResource CreateResourceInstance(IResourceContext resourceContext, Cultur return instance; } - return resourceItem?.Instance; - } - - /// - /// Removes all resources associated with the specified plugin context. - /// - /// The context of the plugin that contains the resources to remove. - public void Remove(IPluginContext pluginContext) - { - if (pluginContext == null) - { - return; - } - - // the plugin has not been registered in the manager - if (!_dictionary.ContainsKey(pluginContext)) - { - return; - } - - foreach (var resourceItem in _dictionary[pluginContext].Values) - { - resourceItem.Dispose(); - } - - _dictionary.Remove(pluginContext); + return resourceItem?.Instance as IResource; } /// diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 1f78d4e..0788357 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebSession { - public class SessionManager : IManager, ISystemComponent + public class SessionManager : IComponentManager, ISystemComponent { /// /// Returns or sets the reference to the context of the host. diff --git a/src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs b/src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs new file mode 100644 index 0000000..3872831 --- /dev/null +++ b/src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebSitemap +{ + /// + /// Contains the registration details for an endpoint, including factory and resolver functions. + /// + public class EndpointRegistration + { + /// + /// Returns or sets the factory function to create a specific endpoint. + /// + public Func Factory { get; set; } + + /// + /// Returns or sets the context resolver function to resolve the corresponding endpoint contexts. + /// + public Func> ContextResolver { get; set; } + + /// + /// Returns or sets the endpoint resolver function to resolve additional endpoint contexts. + /// + public Func> EndpointResolver { get; set; } + + /// + /// Returns or sets the function to handle requests. + /// + public Func HandleRequest { get; set; } + } +} diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs index e05737b..30a1630 100644 --- a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -3,7 +3,6 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap @@ -11,12 +10,25 @@ namespace WebExpress.WebCore.WebSitemap /// /// The interface of the sitemap manager. /// - public interface ISitemapManager : IManager + public interface ISitemapManager : IComponentManager { /// /// Returns the side map. /// - IEnumerable SiteMap { get; } + IEnumerable SiteMap { get; } + + /// + /// Registers an endpoint manager. + /// + /// The type of the endpoint context. + /// The registration details containing the callback functions. + void Register(EndpointRegistration registration) where T : IEndpointContext; + + /// + /// Removes the registration for a specific endpoint manager. + /// + /// The type of the endpoint context. + void Remove() where T : IEndpointContext; /// /// Rebuilds the sitemap. @@ -37,7 +49,7 @@ public interface ISitemapManager : IManager /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// /// Returns the uri taking into account the context or null. - UriResource GetUri(params Parameter[] parameters) where T : IResource; + UriResource GetUri(params Parameter[] parameters) where T : IEndpoint; /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. @@ -53,14 +65,14 @@ public interface ISitemapManager : IManager /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// The module context. /// Returns the uri taking into account the context or null. - UriResource GetUri(IModuleContext moduleContext) where T : IResource; + UriResource GetUri(IModuleContext moduleContext) where T : IEndpoint; /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// The module context. + /// The module context. /// Returns the uri taking into account the context or null. - UriResource GetUri(IResourceContext resourceContext) where T : IResource; + UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint; } } diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index 1824339..18386e5 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebResource; +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap @@ -9,25 +11,27 @@ namespace WebExpress.WebCore.WebSitemap /// public class SearchResult { + private readonly Func _handleRequest; + /// - /// Returns the resource id. + /// Returns the endpoint id. /// - public string ResourceId { get; internal set; } + public string EndpointId { get; internal set; } /// - /// Returns the instance. + /// Returns the context of the endpoint. /// - public IResource Instance { get; internal set; } + public IEndpointContext EndpointContext { get; internal set; } /// - /// Returns the search context. + /// Returns the instance. /// - public SearchContext SearchContext { get; internal set; } + public IEndpoint Instance { get; internal set; } /// - /// Returns the context of the resource. + /// Returns the search context. /// - public IResourceContext ResourceContext { get; internal set; } + public SearchContext SearchContext { get; internal set; } /// /// Returns the context where the resource exists. @@ -49,9 +53,20 @@ public class SearchResult /// /// Initializes a new instance of the class. /// - internal SearchResult() + /// The function to handle requests. + internal SearchResult(Func handleRequest) { + _handleRequest = handleRequest; + } + /// + /// Processing of the resource. + /// + /// The request. + /// The response. + public Response Process(Request request) + { + return _handleRequest(Instance, EndpointContext, request); } } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 2b4bbe3..a8273bb 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -8,7 +8,6 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap @@ -19,28 +18,25 @@ namespace WebExpress.WebCore.WebSitemap public sealed class SitemapManager : ISitemapManager, ISystemComponent { private SitemapNode _root = new(); - private readonly IComponentManager _componentManager; - private readonly IResourceManager _resourceManager; + private readonly Dictionary _registrations = []; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; /// /// Returns the side map. /// - public IEnumerable SiteMap => _root.GetPreOrder() + public IEnumerable SiteMap => _root.GetPreOrder() .Where(x => x != null) - .Select(x => x.ResourceContext); + .Select(x => x.EndpointContext); /// /// Initializes a new instance of the class. /// /// The component manager. - /// The resource manager. /// The reference to the context of the host. - private SitemapManager(IComponentManager componentManager, IResourceManager resourceManager, IHttpServerContext httpServerContext) + private SitemapManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; - _resourceManager = resourceManager as ResourceManager; - _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -49,6 +45,30 @@ private SitemapManager(IComponentManager componentManager, IResourceManager reso ); } + /// + /// Registers an endpoint manager. + /// + /// The type of the endpoint manager. + /// The registration details containing the callback functions. + public void Register(EndpointRegistration registration) where T : IEndpointContext + { + var type = typeof(T); + if (!_registrations.ContainsKey(type)) + { + _registrations[type] = registration; + } + } + + /// + /// Removes the registration for a specific endpoint manager. + /// + /// The type of the endpoint manager. + public void Remove() where T : IEndpointContext + { + var type = typeof(T); + _registrations.Remove(type); + } + /// /// Rebuilds the sitemap. /// @@ -98,10 +118,10 @@ public void Refresh() } // resourcen - var resources = _resourceManager.Resources + var resources = _registrations.Values.SelectMany(x => x.EndpointResolver()) .Select(x => new { - ResourceContext = x, + EndpointContext = x, x.Uri.PathSegments }) .OrderBy(x => x.PathSegments.Count); @@ -111,7 +131,7 @@ public void Refresh() MergeSitemap(newSiteMapNode, CreateSiteMap ( new Queue(item.PathSegments), - item.ResourceContext + item.EndpointContext )); } @@ -140,9 +160,9 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) searchContext ); - if (result != null && result.ResourceContext != null) + if (result != null && result.EndpointContext != null) { - if (!result.ResourceContext.Conditions.Any() || result.ResourceContext.Conditions.All(x => x.Fulfillment(searchContext.HttpContext?.Request))) + if (!result.EndpointContext.Conditions.Any() || result.EndpointContext.Conditions.All(x => x.Fulfillment(searchContext.HttpContext?.Request))) { return result; } @@ -153,72 +173,72 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) } /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// /// Returns the uri taking into account the context or null. - public UriResource GetUri(params Parameter[] parameters) where T : IResource + public UriResource GetUri(params Parameter[] parameters) where T : IEndpoint { - var resourceContexts = _resourceManager.GetResorces(); + var endpointContexts = ResolveEndpointContexts(typeof(T)); var node = _root.GetPreOrder() - .Where(x => resourceContexts.Contains(x.ResourceContext)) + .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.ResourceContext?.Uri.SetParameters(parameters); + return node?.EndpointContext?.Uri.SetParameters(parameters); } /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The resource type. /// The parameters to be considered for the URI. /// Returns the URI taking into account the context, or null if no valid URI is found. public UriResource GetUri(Type resourceType, params Parameter[] parameters) { - var resourceContexts = _resourceManager.GetResorces(resourceType); + var endpointContexts = ResolveEndpointContexts(resourceType); var node = _root.GetPreOrder() - .Where(x => resourceContexts.Contains(x.ResourceContext)) + .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.ResourceContext?.Uri.SetParameters(parameters); + return node?.EndpointContext?.Uri.SetParameters(parameters); } /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// The module context. /// Returns the uri taking into account the context or null. - public UriResource GetUri(IModuleContext moduleContext) where T : IResource + public UriResource GetUri(IModuleContext moduleContext) where T : IEndpoint { - var resourceContexts = _resourceManager.GetResorces(moduleContext); + var endpointContexts = ResolveEndpointContexts(typeof(T), moduleContext); var node = _root.GetPreOrder() - .Where(x => resourceContexts.Contains(x.ResourceContext)) + .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.ResourceContext?.Uri; + return node?.EndpointContext?.Uri; } /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// The module context. + /// The endpoint context. /// Returns the uri taking into account the context or null. - public UriResource GetUri(IResourceContext resourceContext) where T : IResource + public UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint { - var resourceContexts = _resourceManager.GetResorces(resourceContext.ModuleContext) - .Where(x => x.ResourceId.Equals(resourceContext.ResourceId, StringComparison.OrdinalIgnoreCase)); + var endpointContexts = ResolveEndpointContexts(typeof(T), endpointContext.ModuleContext) + .Where(x => x.EndpointId.Equals(endpointContext.EndpointId, StringComparison.OrdinalIgnoreCase)); var node = _root.GetPreOrder() - .Where(x => resourceContexts.Contains(x.ResourceContext)) + .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.ResourceContext?.Uri; + return node?.EndpointContext?.Uri; } /// @@ -325,7 +345,7 @@ private static SitemapNode CreateSiteMap SitemapNode parent ) { - var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + var pathSegment = contextPathSegments.Count != 0 ? contextPathSegments.Dequeue() : null; if (pathSegment == null) { @@ -352,16 +372,16 @@ SitemapNode parent /// by the number of path segments in ascending order. /// /// The path segments of the context path. - /// The resource context. + /// The endpoint context. /// The sitemap root node. private static SitemapNode CreateSiteMap ( Queue contextPathSegments, - IResourceContext resourceContext + IEndpointContext endpointContext ) { var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; - var next = CreateSiteMap(contextPathSegments, resourceContext, root); + var next = CreateSiteMap(contextPathSegments, endpointContext, root); if (next != null) { @@ -373,17 +393,17 @@ IResourceContext resourceContext /// /// Creates the sitemap. Works recursively. - /// It is important for the algorithm that the addition of resources is sorted + /// It is important for the algorithm that the addition of endpoint is sorted /// by the number of path segments in ascending order. /// /// The path segments of the context path. - /// The resource context. + /// The endpoint context. /// The parent node or null if root. /// The sitemap parent node. private static SitemapNode CreateSiteMap ( Queue contextPathSegments, - IResourceContext resourceContext, + IEndpointContext endpointContext, SitemapNode parent = null ) { @@ -398,12 +418,12 @@ private static SitemapNode CreateSiteMap { PathSegment = pathSegment, Parent = parent, - ResourceContext = resourceContext + EndpointContext = endpointContext }; if (contextPathSegments.Any()) { - node.Children.Add(CreateSiteMap(contextPathSegments, resourceContext, node)); + node.Children.Add(CreateSiteMap(contextPathSegments, endpointContext, node)); } return node; @@ -422,9 +442,9 @@ private static void MergeSitemap(SitemapNode first, SitemapNode second) { foreach (var fc in first.Children.Where(x => x.PathSegment.Equals(sc.PathSegment))) { - if (fc.ResourceContext == null) + if (fc.EndpointContext == null) { - fc.ResourceContext = sc.ResourceContext; + fc.EndpointContext = sc.EndpointContext; //fc.Parent = sc.Parent; } @@ -455,8 +475,8 @@ private SearchResult SearchNode SearchContext searchContext ) { - var pathSegment = inPathSegments.Any() ? inPathSegments.Dequeue() : null; - var nextPathSegment = inPathSegments.Any() ? inPathSegments.Peek() : null; + var pathSegment = inPathSegments.Count != 0 ? inPathSegments.Dequeue() : null; + var nextPathSegment = inPathSegments.Count != 0 ? inPathSegments.Peek() : null; if (IsMatched(node, pathSegment)) { @@ -466,28 +486,36 @@ SearchContext searchContext variable.Value = pathSegment; } + var type = node.EndpointContext?.GetType(); + var registration = default(EndpointRegistration); + + if (type != null && _registrations.TryGetValue(type, out var _registration)) + { + registration = _registration; + } + outPathSegments.Enqueue(copy); if (nextPathSegment == null) { - return new SearchResult() + return new SearchResult(registration?.HandleRequest) { - ResourceId = node.ResourceContext.ResourceId, - ResourceContext = node.ResourceContext, + EndpointId = node.EndpointContext.EndpointId, + EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource(outPathSegments.ToArray()), - Instance = CreateInstance(node, new UriResource(outPathSegments.ToArray()), searchContext), + Uri = new UriResource([.. outPathSegments]), + Instance = CreateEndpoint(node, new UriResource([.. outPathSegments]), searchContext) }; } - else if (node.IsLeaf && nextPathSegment != null && node.ResourceContext != null && node.ResourceContext.IncludeSubPaths) + else if (node.IsLeaf && nextPathSegment != null && node.EndpointContext != null && node.EndpointContext.IncludeSubPaths) { - return new SearchResult() + return new SearchResult(registration?.HandleRequest) { - ResourceId = node.ResourceContext.ResourceId, - ResourceContext = node.ResourceContext, + EndpointId = node.EndpointContext.EndpointId, + EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource(outPathSegments.ToArray()), - Instance = CreateInstance(node, new UriResource(outPathSegments.ToArray()), searchContext), + Uri = new UriResource([.. outPathSegments]), + Instance = CreateEndpoint(node, new UriResource([.. outPathSegments]), searchContext) }; } @@ -498,11 +526,11 @@ SearchContext searchContext } // 404 - return new SearchResult() + return new SearchResult(null) { - ResourceContext = node.ResourceContext, + EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource(outPathSegments.ToArray()) + Uri = new UriResource([.. outPathSegments]) }; } @@ -512,21 +540,43 @@ SearchContext searchContext /// The sitemap node. /// The uri. /// The search context. - /// The instance or null. - private IResource CreateInstance(SitemapNode node, UriResource uri, SearchContext context) + /// The created endpoint. + private IEndpoint CreateEndpoint(SitemapNode node, UriResource uri, SearchContext context) { - if (node == null || node.ResourceContext == null) + if (node == null || node.EndpointContext == null) { return null; } - var instance = _resourceManager.CreateResourceInstance(node.ResourceContext, context.Culture); + var type = node.EndpointContext.GetType(); + + if (_registrations.TryGetValue(type, out var registration)) + { + return registration.Factory(node.EndpointContext, uri, context.Culture); + } + + throw new InvalidOperationException($"No factory registered for type {type}"); + } - //if (instance is IPage page) - //{ - //} + /// + /// Resolves the endpoint context for the specified endpoint. + /// + /// The type of the endpoint. + /// The optional module context. + /// An enumerable of the corresponding endpoint contexts. + private IEnumerable ResolveEndpointContexts(Type endpointType, IModuleContext moduleContext = null) + { + foreach (var contextResolver in _registrations.Values.Select(x => x.ContextResolver)) + { + var res = contextResolver(endpointType, moduleContext); + + if (res.Any()) + { + return res; + } + } - return instance; + return []; } /// @@ -567,7 +617,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ( "webexpress:sitemapmanager.preorder", " " + x.ToString().PadRight(60), - x.ResourceContext?.ResourceId ?? "" + x.EndpointContext?.EndpointId ?? "" )); foreach (var node in preorder) diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs index a014c1f..2c2c134 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap @@ -16,14 +16,14 @@ public class SitemapNode public IUriPathSegment PathSegment { get; internal set; } /// - /// Returns the context of the resource. + /// Returns the context of the endpoint. /// - public IResourceContext ResourceContext { get; internal set; } + public IEndpointContext EndpointContext { get; internal set; } /// /// Returns the child nodes. /// - public ICollection Children { get; private set; } = new List(); + public ICollection Children { get; private set; } = []; /// /// Returns the parent node. @@ -127,7 +127,7 @@ public SitemapNode Copy() var node = new SitemapNode() { PathSegment = PathSegment, - ResourceContext = ResourceContext, + EndpointContext = EndpointContext, Parent = Parent, Children = Children.Select(x => x.Copy()).ToList() }; @@ -144,7 +144,7 @@ public override string ToString() return Path.FirstOrDefault()?.PathSegment + string.Join ( "/", - Path.Where(x => !(x.PathSegment is UriPathSegmentRoot)) + Path.Where(x => x.PathSegment is not UriPathSegmentRoot) .Select(x => x.PathSegment?.ToString()) ); } diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs index 4aff730..e6ac3fd 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs @@ -1,5 +1,5 @@ using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage @@ -12,7 +12,7 @@ public interface IStatusPage /// /// Returns the resource context where the resource exists. /// - IResourceContext ResourceContext { get; } + IPageContext ResourceContext { get; } /// /// Returns or sets the status code. @@ -38,7 +38,7 @@ public interface IStatusPage /// Initialization /// /// The context of the resource. - void Initialization(IResourceContext resourceContext); + void Initialization(IPageContext resourceContext); /// /// Processing of the resource. diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 092f9eb..9203a36 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -4,8 +4,8 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage @@ -13,9 +13,9 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Management of status pages. /// - public class StatusPageManager : IManagerPlugin, ISystemComponent + public class StatusPageManager : IComponentManagerPlugin, ISystemComponent { - private readonly IComponentManager _componentManager; + private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly StatusPageDictionary _dictionary = []; private readonly StatusPageDictionaryItem _defaults = []; @@ -23,12 +23,12 @@ public class StatusPageManager : IManagerPlugin, ISystemComponent /// /// An event that fires when an status page is added. /// - public event EventHandler AddStatusPage; + public event EventHandler AddStatusPage; /// /// An event that fires when an status page is removed. /// - public event EventHandler RemoveStatusPage; + public event EventHandler RemoveStatusPage; /// /// Returns all status pages. @@ -49,7 +49,7 @@ public class StatusPageManager : IManagerPlugin, ISystemComponent /// /// The component manager. /// The reference to the context of the host. - internal StatusPageManager(IComponentManager componentManager, IHttpServerContext httpServerContext) + internal StatusPageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentManager = componentManager; @@ -316,7 +316,7 @@ public void Remove(IPluginContext pluginContext) /// Raises the AddStatusPage event. /// /// The status page. - private void OnAddStatusPage(IResourceContext statusPage) + private void OnAddStatusPage(IPageContext statusPage) { AddStatusPage?.Invoke(this, statusPage); } @@ -325,7 +325,7 @@ private void OnAddStatusPage(IResourceContext statusPage) /// Raises the RemoveComponent event. /// /// The status page. - private void OnRemoveStatusPage(IResourceContext statusPage) + private void OnRemoveStatusPage(IPageContext statusPage) { RemoveStatusPage?.Invoke(this, statusPage); } diff --git a/src/WebExpress.WebCore/WebTask/Task.cs b/src/WebExpress.WebCore/WebTask/Task.cs index c30444c..0492a71 100644 --- a/src/WebExpress.WebCore/WebTask/Task.cs +++ b/src/WebExpress.WebCore/WebTask/Task.cs @@ -97,7 +97,7 @@ public void Run() OnFinish(); - WebEx.ComponentManager.TaskManager.RemoveTask(this); + WebEx.ComponentHub.TaskManager.RemoveTask(this); }), TokenSource.Token); } @@ -111,7 +111,7 @@ public void Cancel() State = TaskState.Canceled; - WebEx.ComponentManager.TaskManager.RemoveTask(this); + WebEx.ComponentHub.TaskManager.RemoveTask(this); } } } diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 8d5b844..3c56a66 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebTask /// /// Management of ad-hoc tasks. /// - public class TaskManager : IManager, ISystemComponent + public class TaskManager : IComponentManager, ISystemComponent { /// /// Returns or sets the reference to the context of the host. From d5f4d6508fc365cc3bb29b458774d34c417f49e0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 7 Oct 2024 23:31:25 +0200 Subject: [PATCH 020/162] bugfixes --- .../Manager/UnitTestSitemapManager.cs | 8 +++----- src/WebExpress.WebCore.Test/TestPageA1X.cs | 9 +-------- src/WebExpress.WebCore/WebPage/PageManager.cs | 16 +++++++++++++--- .../WebSitemap/SearchResult.cs | 7 ++++++- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 9253a7d..452d491 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,12 +1,11 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSitemap; namespace WebExpress.WebCore.Test.Manager { /// - /// Test the resource manager. + /// Test the sitemap manager. /// [Collection("NonParallelTests")] public class UnitTestSitemapManager @@ -44,7 +43,6 @@ public void SearchResource(string uri, string id) var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); var context = UnitTestControlFixture.CreateHttpContext(); componentHub.SitemapManager.Refresh(); - var map = componentHub.SitemapManager.ToString(); // test execution var searchResult = componentHub.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() @@ -54,6 +52,8 @@ public void SearchResource(string uri, string id) HttpContext = context }); + searchResult.Process(UnitTestControlFixture.CrerateRequest()); + Assert.Equal(id, searchResult.EndpointId); } @@ -71,7 +71,6 @@ public void GetUri(Type resourceType, string expected) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var context = UnitTestControlFixture.CreateHttpContext(); componentHub.SitemapManager.Refresh(); // test execution @@ -88,7 +87,6 @@ public void IsIComponentManager() { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var pluginManager = componentHub.PluginManager as PluginManager; // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SitemapManager.GetType())); diff --git a/src/WebExpress.WebCore.Test/TestPageA1X.cs b/src/WebExpress.WebCore.Test/TestPageA1X.cs index 0139b0d..3957452 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1X.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1X.cs @@ -44,9 +44,8 @@ public void Redirecting(string uri) /// Processing of the page. /// /// The context for rendering the page. - public void Process(RenderContext context) + public void Process(IRenderContext context) { - } /// @@ -54,12 +53,6 @@ public void Process(RenderContext context) /// public void Dispose() { - throw new NotImplementedException(); - } - - public void Process(IRenderContext context) - { - throw new NotImplementedException(); } } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 3506860..be8d2ec 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -81,10 +81,20 @@ private PageManager(IComponentHub componentManager, IHttpServerContext httpServe EndpointResolver = () => Pages, HandleRequest = (endpoint, endpontContext, request) => { - var typeOfT = endpoint.GetType().GetGenericArguments()[0]; - object[] parameters = [endpoint, endpontContext as IPageContext, request]; + var type = endpoint.GetType(); + var context = default(IRenderContext); - var context = (IRenderContext)Activator.CreateInstance(typeOfT, parameters); + if (type.IsGenericType) + { + var typeOfT = type.GetGenericArguments()[0]; + object[] parameters = [endpoint as IPage, endpontContext as IPageContext, request]; + + context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; + } + else + { + context = new RenderContext(endpoint as IPage, endpontContext as IPageContext, request); + } (endpoint as IPage).Process(context); diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index 18386e5..7e93a78 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -66,7 +66,12 @@ internal SearchResult(Func handl /// The response. public Response Process(Request request) { - return _handleRequest(Instance, EndpointContext, request); + if (_handleRequest != null) + { + return _handleRequest(Instance, EndpointContext, request); + } + + return new ResponseBadRequest(); } } } From 115add59f8495c46398fb4f5565544d0f3ff898c Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 8 Oct 2024 21:31:46 +0200 Subject: [PATCH 021/162] added restapimanager and tests - general bug fixes --- .../Manager/UnitTestPageManager.cs | 8 +- .../Manager/UnitTestResourceManager.cs | 3 +- .../Manager/UnitTestRestApiManager.cs | 108 ++++ .../Manager/UnitTestSitemapManager.cs | 4 +- src/WebExpress.WebCore.Test/TestPageA1X.cs | 11 + src/WebExpress.WebCore.Test/TestPageA1Y.cs | 69 +++ src/WebExpress.WebCore.Test/TestPageA1Z.cs | 50 ++ .../TestResourceA1X.cs | 26 +- .../TestResourceA1Y.cs | 35 +- .../TestResourceA2X.cs | 26 +- .../TestResourceAB1X.cs | 26 +- src/WebExpress.WebCore.Test/TestRestApiA1X.cs | 79 +++ src/WebExpress.WebCore.Test/TestRestApiA1Y.cs | 79 +++ src/WebExpress.WebCore.Test/TestRestApiA1Z.cs | 79 +++ .../Internationalization/de | 6 + .../Internationalization/en | 6 + .../WebAttribute/ParentAttribute.cs | 4 +- .../WebComponent/ComponentHub.cs | 10 + .../WebComponent/IComponentHub.cs | 7 + .../WebPage/Model/PageItem.cs | 2 +- src/WebExpress.WebCore/WebPage/Page.cs | 13 +- .../WebPage/RenderContext.cs | 10 +- .../WebResource/Model/ResourceItem.cs | 8 +- .../WebResource/ResourceManager.cs | 2 - src/WebExpress.WebCore/WebRestAPI/IRestApi.cs | 36 ++ .../WebRestAPI/IRestApiContext.cs | 11 + .../WebRestAPI/IRestApiManager.cs | 91 +++ .../WebRestAPI/Model/RestApiDictionary.cs | 13 + .../WebRestAPI/Model/RestApiItem.cs | 224 ++++++++ src/WebExpress.WebCore/WebRestAPI/RestApi.cs | 60 ++ .../WebRestAPI/RestApiContext.cs | 117 ++++ .../WebRestAPI/RestApiManager.cs | 532 ++++++++++++++++++ 32 files changed, 1648 insertions(+), 107 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestPageA1Y.cs create mode 100644 src/WebExpress.WebCore.Test/TestPageA1Z.cs create mode 100644 src/WebExpress.WebCore.Test/TestRestApiA1X.cs create mode 100644 src/WebExpress.WebCore.Test/TestRestApiA1Y.cs create mode 100644 src/WebExpress.WebCore.Test/TestRestApiA1Z.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/IRestApi.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/RestApi.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index 23d3b94..b00698b 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - Assert.Equal(1, componentHub.PageManager.Pages.Count()); + Assert.Equal(3, componentHub.PageManager.Pages.Count()); } /// @@ -45,6 +45,8 @@ public void Remove() /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webexpress.webcore.test.testpagea1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "webexpress.webcore.test.testpagea1y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "webexpress.webcore.test.testpagea1z")] public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions @@ -61,6 +63,8 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "webindex:homepage.label")] public void Title(Type applicationType, Type moduleType, Type resourceType, string id) { @@ -78,6 +82,8 @@ public void Title(Type applicationType, Type moduleType, Type resourceType, stri /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "/aca/mca")] public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index 1f9180a..3ce93e7 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -50,6 +50,7 @@ public void Remove() [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webexpress.webcore.test.testresourcea2x")] [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestPageA1X), null)] public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { @@ -59,7 +60,7 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string var resource = componentHub.ResourceManager.GetResorce(module, resourceType); // test execution - Assert.Equal(id, resource.EndpointId); + Assert.Equal(id, resource?.EndpointId); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs new file mode 100644 index 0000000..d7c043b --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -0,0 +1,108 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebRestApi; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the rest api manager. + /// + [Collection("NonParallelTests")] + public class UnitTestRestApiManager + { + /// + /// Test the register function of the rest api manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.Equal(3, componentHub.RestApiManager.RestApis.Count()); + } + + /// + /// Test the remove function of the rest api manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var apiManager = componentHub.RestApiManager as RestApiManager; + + // test execution + apiManager.Remove(plugin); + + Assert.Empty(componentHub.RestApiManager.RestApis); + } + + /// + /// Test the id property of the rest api. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "webexpress.webcore.test.testrestapia1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "webexpress.webcore.test.testrestapia1y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "webexpress.webcore.test.testrestapia1z")] + public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + + // test execution + Assert.Equal(id, api.EndpointId); + } + + /// + /// Test the context path property of the rest api. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "/aca/mca")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "/aca/mca/ra1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "/aca/mca/ra1x/ra1y")] + public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + + // test execution + Assert.Equal(id, api.ContextPath); + } + + /// + /// Tests whether the rest api manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.RestApiManager.GetType())); + } + + /// + /// Tests whether the rest api context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var api in componentHub.RestApiManager.RestApis) + { + Assert.True(typeof(IContext).IsAssignableFrom(api.GetType()), $"Api context {api.GetType().Name} does not implement IContext."); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 452d491..cda6e67 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,7 +22,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(14, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(19, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -66,6 +66,8 @@ public void SearchResource(string uri, string id) [InlineData(typeof(TestResourceA2X), "/aca/a2x")] [InlineData(typeof(TestResourceAB1X), "/aca/mcab/ab1x")] [InlineData(typeof(TestPageA1X), "/aca/mca/pa1x")] + [InlineData(typeof(TestPageA1Y), "/aca/mca/pa1y")] + [InlineData(typeof(TestPageA1Z), "/aca/mca/pa1z")] public void GetUri(Type resourceType, string expected) { diff --git a/src/WebExpress.WebCore.Test/TestPageA1X.cs b/src/WebExpress.WebCore.Test/TestPageA1X.cs index 3957452..db80211 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1X.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1X.cs @@ -29,6 +29,12 @@ public sealed class TestPageA1X : IPage public TestPageA1X(IPageContext pageContext) { PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } } /// @@ -46,6 +52,11 @@ public void Redirecting(string uri) /// The context for rendering the page. public void Process(IRenderContext context) { + // test the context + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } } /// diff --git a/src/WebExpress.WebCore.Test/TestPageA1Y.cs b/src/WebExpress.WebCore.Test/TestPageA1Y.cs new file mode 100644 index 0000000..d46c94e --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestPageA1Y.cs @@ -0,0 +1,69 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Segment("pa1y", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestPageA1Y : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Instillation of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public TestPageA1Y(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + public void Process(IRenderContext context) + { + // test the context + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPageA1Z.cs b/src/WebExpress.WebCore.Test/TestPageA1Z.cs new file mode 100644 index 0000000..3ada69e --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestPageA1Z.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Segment("pa1z", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestPageA1Z : Page + { + /// + /// Instillation of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + private TestPageA1Z(IPageContext pageContext) + : base(pageContext) + { + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + public override void Process(IRenderContext context) + { + // test the context + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceA1X.cs b/src/WebExpress.WebCore.Test/TestResourceA1X.cs index c748481..7526fd8 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1X.cs @@ -26,26 +26,6 @@ public TestResourceA1X(IResourceContext resourceContext) } } - /// - /// Post-processes the request and response. - /// - /// The request. - /// The response. - /// The processed response. - public Response PostProcess(WebMessage.Request request, Response response) - { - return null; - } - - /// - /// Pre-processes the request. - /// - /// The request. - public void PreProcess(WebMessage.Request request) - { - - } - /// /// Processes the request. /// @@ -53,6 +33,12 @@ public void PreProcess(WebMessage.Request request) /// The processed response. public Response Process(WebMessage.Request request) { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + return null; } diff --git a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs index 00c762a..73d3a59 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs @@ -19,26 +19,6 @@ public TestResourceA1Y() { } - /// - /// Post-processes the request and response. - /// - /// The request. - /// The response. - /// The processed response. - public Response PostProcess(WebMessage.Request request, Response response) - { - return null; - } - - /// - /// Pre-processes the request. - /// - /// The request. - public void PreProcess(WebMessage.Request request) - { - - } - /// /// Processes the request. /// @@ -46,16 +26,13 @@ public void PreProcess(WebMessage.Request request) /// The processed response. public Response Process(WebMessage.Request request) { - return null; - } - - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + return null; } /// diff --git a/src/WebExpress.WebCore.Test/TestResourceA2X.cs b/src/WebExpress.WebCore.Test/TestResourceA2X.cs index 2fb04b1..8dd75a5 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA2X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA2X.cs @@ -33,26 +33,6 @@ public TestResourceA2X(IResourceManager resourceManager, IResourceContext resour } } - /// - /// Post-processes the request and response. - /// - /// The request. - /// The response. - /// The processed response. - public Response PostProcess(WebMessage.Request request, Response response) - { - return null; - } - - /// - /// Pre-processes the request. - /// - /// The request. - public void PreProcess(WebMessage.Request request) - { - - } - /// /// Processes the request. /// @@ -60,6 +40,12 @@ public void PreProcess(WebMessage.Request request) /// The processed response. public Response Process(WebMessage.Request request) { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + return null; } diff --git a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs index dec72db..87c166f 100644 --- a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs @@ -32,26 +32,6 @@ public TestResourceAB1X(IResourceContext resourceContext, IResourceManager resou } } - /// - /// Post-processes the request and response. - /// - /// The request. - /// The response. - /// The processed response. - public Response PostProcess(WebMessage.Request request, Response response) - { - return null; - } - - /// - /// Pre-processes the request. - /// - /// The request. - public void PreProcess(WebMessage.Request request) - { - - } - /// /// Processes the request. /// @@ -59,6 +39,12 @@ public void PreProcess(WebMessage.Request request) /// The processed response. public Response Process(WebMessage.Request request) { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + return null; } diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs new file mode 100644 index 0000000..599606f --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs @@ -0,0 +1,79 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebRestApi; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourcea1x.label")] + [Segment("ra1x", "webindex:homepage.label")] + [ContextPath(null)] + [Module] + public sealed class TestRestApiA1X : IRestApi + { + /// + /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// + /// The context of the restapi resource. + public TestRestApiA1X(IRestApiContext restApiContext) + { + // test the injection + if (restApiContext == null) + { + throw new ArgumentNullException(nameof(restApiContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Creates data. + /// + /// The request. + public void CreateData(WebMessage.Request request) + { + + } + + /// + /// Gets data. + /// + /// The data. + public object GetData() + { + return null; + } + + /// + /// Updates data. + /// + /// The request. + public void UpdateData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Deletes data. + /// + /// The request. + public void DeleteData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs new file mode 100644 index 0000000..b93b3f4 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs @@ -0,0 +1,79 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebRestApi; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourcea1x.label")] + [Segment("ra1y", "webindex:homepage.label")] + [Parent] + [Module] + public sealed class TestRestApiA1Y : IRestApi + { + /// + /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// + /// The context of the restapi resource. + public TestRestApiA1Y(IRestApiContext restApiContext) + { + // test the injection + if (restApiContext == null) + { + throw new ArgumentNullException(nameof(restApiContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Creates data. + /// + /// The request. + public void CreateData(WebMessage.Request request) + { + + } + + /// + /// Gets data. + /// + /// The data. + public object GetData() + { + return null; + } + + /// + /// Updates data. + /// + /// The request. + public void UpdateData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Deletes data. + /// + /// The request. + public void DeleteData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs new file mode 100644 index 0000000..a9a8a86 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs @@ -0,0 +1,79 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebRestApi; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:resourcea1x.label")] + [Segment("ra1y", "webindex:homepage.label")] + [Parent] + [Module] + public sealed class TestRestApiA1Z : IRestApi + { + /// + /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// + /// The context of the restapi resource. + public TestRestApiA1Z(IRestApiContext restApiContext) + { + // test the injection + if (restApiContext == null) + { + throw new ArgumentNullException(nameof(restApiContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Creates data. + /// + /// The request. + public void CreateData(WebMessage.Request request) + { + + } + + /// + /// Gets data. + /// + /// The data. + public object GetData() + { + return null; + } + + /// + /// Updates data. + /// + /// The request. + public void UpdateData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Deletes data. + /// + /// The request. + public void DeleteData(WebMessage.Request request) + { + // test the request + if (request == null) + { + throw new ArgumentNullException(nameof(request), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index a96dfea..b44178b 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -107,6 +107,12 @@ pagemanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. pagemanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. pagemanager.resource=Seite: '{0}' für das Modul '{1}' +restapimanager.initialization=Der RestApiManager wurde initialisiert. +restapimanager.moduleless=Die Seite '{0}' besitzt keine Angaben zum Modul. +restapimanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. +restapimanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. +restapimanager.resource=Seite: '{0}' für das Modul '{1}' + statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. statuspagemanager.register=Status {0} wurde registriert und der Statusseite '{1}' zugewiesen. statuspagemanager.duplicat=Der Status {0} wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index d066110..c0f2aa6 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -107,6 +107,12 @@ pagemanager.addresource=The page '{0}' has been registered in the module '{1}'. pagemanager.addresource.duplicate=The page '{0}' of module '{1}' has already been added. pagemanager.resource=Page: '{0}' for module '{1}' +restapimanager.initialization=The REST API manager has been initialized. +restapimanager.moduleless=The REST API '{0}' does not have any information about the module. +restapimanager.addresource=The REST API '{0}' has been registered in the module '{1}'. +restapimanager.addresource.duplicate=The REST API '{0}' of module '{1}' has already been added. +restapimanager.resource=REST API: '{0}' for module '{1}' + statuspagemanager.initialization=The status page manager has been initialized. statuspagemanager.register=Status {0} has been registered and assigned to the status page '{1}'. statuspagemanager.duplicat=The status {0} has already been registered. Therefore, the status page '{1}' is not used. diff --git a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs index 30c36d9..e8477a9 100644 --- a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs @@ -1,10 +1,10 @@ using System; -using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebAttribute { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ParentAttribute : Attribute, IResourceAttribute where T : class, IResource + public class ParentAttribute : Attribute, IResourceAttribute where T : class, IEndpoint { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 88116cb..a87c6fe 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -11,6 +11,7 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebRestApi; using WebExpress.WebCore.WebSession; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; @@ -29,6 +30,7 @@ public class ComponentHub : IComponentHub private readonly ModuleManager _moduleManager; private readonly ResourceManager _resourceManager; private readonly PageManager _pageManager; + private readonly RestApiManager _restApiManager; private readonly SitemapManager _sitemapManager; /// @@ -64,6 +66,7 @@ public class ComponentHub : IComponentHub _sitemapManager, _resourceManager, _pageManager, + _restApiManager, EventManager, JobManager, StatusPageManager, @@ -132,6 +135,12 @@ public class ComponentHub : IComponentHub /// The instance of the page manager. public IPageManager PageManager => _pageManager; + /// + /// Returns the rest api manager. + /// + /// The instance of the rest api manager. + public IRestApiManager RestApiManager => _restApiManager; + /// /// Returns the sitemap manager. /// @@ -174,6 +183,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; _pageManager = CreateInstance(typeof(PageManager)) as PageManager; + _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; StatusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 932d055..ccbf411 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -10,6 +10,7 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebRestApi; using WebExpress.WebCore.WebSession; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; @@ -101,6 +102,12 @@ public interface IComponentHub : IComponentManager /// The instance of the page manager. IPageManager PageManager { get; } + /// + /// Returns the rest api manager. + /// + /// The instance of the rest api manager. + IRestApiManager RestApiManager { get; } + /// /// Returns the sitemap manager. /// diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 7afb0d1..c005a66 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -163,7 +163,7 @@ public void AddModule(IModuleContext moduleContext) } /// - /// Remove an module assignment + /// Remove an module assignment. /// /// The context of the module. public void DetachModule(IModuleContext moduleContext) diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index bb0b567..ccc4f3d 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -1,25 +1,28 @@ -using WebExpress.WebCore.WebResource; - -namespace WebExpress.WebCore.WebPage +namespace WebExpress.WebCore.WebPage { /// /// The prototype of a website. /// /// An implementation of the visualization tree. - public abstract class Page : Resource, IPage where T : RenderContext, new() + public abstract class Page : IPage where T : IRenderContext, new() { /// /// Returns or sets the page title. /// public string Title { get; set; } + /// + /// Returns the page context. + /// + public IPageContext PageContext { get; private set; } + /// /// Initializes a new instance of the class. /// /// The context of the page. public Page(IPageContext pageContext) { - + PageContext = pageContext; } /// diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 04daddf..899fb53 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -39,6 +39,14 @@ public class RenderContext : IRenderContext /// public IVisualTree VisualTree { get; protected set; } + /// + /// Initializes a new instance of the class. + /// + public RenderContext() + { + VisualTree = CreateVisualTree(); + } + /// /// Initializes a new instance of the class. /// @@ -46,11 +54,11 @@ public class RenderContext : IRenderContext /// The context of the associated page. /// The request associated with the rendering context. public RenderContext(IPage page, IPageContext pageContext, Request request) + : this() { Page = page; PageContext = pageContext; Request = request; - VisualTree = CreateVisualTree(); } /// diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index f045d05..599bc83 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -36,7 +36,7 @@ internal class ResourceItem : IDisposable /// /// Returns or sets the resource title. /// - public string Title { get; set; } + //public string Title { get; set; } /// /// Returns or sets the parent id. @@ -63,7 +63,7 @@ internal class ResourceItem : IDisposable /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// - public IReadOnlyList Scopes { get; set; } + //public IReadOnlyList Scopes { get; set; } /// /// Returns or sets the paths of the resource. @@ -142,7 +142,7 @@ public void AddModule(IModuleContext moduleContext) // create context var resourceContext = new ResourceContext(moduleContext, _resourceManager, this) { - Scopes = Scopes, + //Scopes = Scopes, Conditions = Conditions, EndpointId = ResourceId, Cache = Cache, @@ -163,7 +163,7 @@ public void AddModule(IModuleContext moduleContext) } /// - /// Remove an module assignment + /// Remove an module assignment. /// /// The context of the module. public void DetachModule(IModuleContext moduleContext) diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 487b3c0..cecd2cc 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -196,11 +196,9 @@ public void Register(IPluginContext pluginContext) var resourceItem = new ResourceItem(_componentManager.ResourceManager) { ResourceId = id, - Title = title, ParentId = parent, ResourceClass = resourceType, ModuleId = moduleId, - Scopes = scopes, Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs new file mode 100644 index 0000000..a813cf5 --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs @@ -0,0 +1,36 @@ +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// Defines the contract for a rest api resource. + /// + public interface IRestApi : IEndpoint + { + /// + /// Creates data. + /// + /// The request. + void CreateData(Request request); + + /// + /// Gets data. + /// + /// The data. + object GetData(); + + /// + /// Updates data. + /// + /// The request. + void UpdateData(Request request); + + /// + /// Deletes data. + /// + /// The id of the data to delete. + /// The request. + void DeleteData(Request request); + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs new file mode 100644 index 0000000..d3f7d3a --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// Defines the context for a rest api resource, providing access to various related contexts and properties. + /// + public interface IRestApiContext : IEndpointContext + { + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs new file mode 100644 index 0000000..ab9b335 --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// The page manager manages rest api resources, which can be called with a URI (Uniform Resource Identifier). + /// + public interface IRestApiManager : IEndpointManager + { + /// + /// An event that fires when an rest api is added. + /// + event EventHandler AddRestApi; + + /// + /// An event that fires when an rest api is removed. + /// + event EventHandler RemoveRestApi; + + /// + /// Returns all rest api contexts. + /// + IEnumerable RestApis { get; } + + /// + /// Returns an enumeration of all containing rest api contexts of a plugin. + /// + /// A context of a plugin whose rest apis are to be registered. + /// An enumeration of rest api contexts. + IEnumerable GetRestApi(IPluginContext pluginContext); + + /// + /// Returns an enumeration of rest api contextes. + /// + /// The rest api type. + /// An enumeration of rest api contextes. + IEnumerable GetRestApi() where T : IRestApi; + + /// + /// Returns an enumeration of rest api contextes. + /// + /// The rest api type. + /// An enumeration of rest api contextes. + IEnumerable GetRestApi(Type pageType); + + /// + /// Returns an enumeration of rest api contextes. + /// + /// The rest api type. + /// The context of the module. + /// An enumeration of rest api contextes. + IEnumerable GetRestApi(Type restApiType, IModuleContext moduleContext); + + /// + /// Returns an enumeration of rest api contextes. + /// + /// The rest api type. + /// The context of the module. + /// An enumeration of rest api contextes. + IEnumerable GetRestApi(IModuleContext moduleContext) where T : IRestApi; + + /// + /// Returns the rest api context. + /// + /// The context of the module. + /// The rest api id. + /// An rest api context or null. + IRestApiContext GetRestApi(IModuleContext moduleContext, string restApiId); + + /// + /// Returns the rest api context. + /// + /// The context of the module. + /// The rest api type. + /// An rest api context or null. + IRestApiContext GetRestApi(IModuleContext moduleContext, Type restApiType); + + /// + /// Returns the rest api context. + /// + /// The application id. + /// The module id. + /// The rest api id. + /// An rest api context or null. + IRestApiContext GetRestApi(string applicationId, string moduleId, string restApiId); + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs new file mode 100644 index 0000000..2e92c21 --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebRestApi.Model +{ + /// + /// key = plugin context + /// value = { key = resource id, value = ressource item } + /// + internal class RestApiDictionary : Dictionary> + { + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs new file mode 100644 index 0000000..ed913da --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebRestApi.Model +{ + /// + /// A rest api resource that contains meta information about a rest api resource. + /// + internal class RestApiItem : IDisposable + { + private readonly IRestApiManager _restApiManager; + + /// + /// An event that fires when an rest api resource is added. + /// + public event EventHandler AddRestApi; + + /// + /// An event that fires when an rest api resource is removed. + /// + public event EventHandler RemoveRestApi; + + /// + /// Returns or sets the rest api resource id. + /// + public string RestApiId { get; set; } + + /// + /// Returns or sets the parent id. + /// + public string ParentId { get; set; } + + /// + /// Returns or sets the type of rest api resource. + /// + public Type RestApiClass { get; set; } + + /// + /// Returns or sets the instance of the rest api resource, if the rest api resource is cached, otherwise null. + /// + public IRestApi Instance { get; set; } + + /// + /// Returns or sets the module id. + /// + public string ModuleId { get; set; } + + /// + /// Returns or sets the paths of the resource. + /// + public UriResource ContextPath { get; set; } + + /// + /// Returns or sets the path segment. + /// + public IUriPathSegment PathSegment { get; internal set; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; set; } + + /// + /// Returns the conditions that must be met for the rest api resource to be active. + /// + public ICollection Conditions { get; set; } + + /// + /// Returns whether the resource is created once and reused each time it is called. + /// + public bool Cache { get; set; } + + /// + /// Returns whether it is a optional rest api resource. + /// + public bool Optional { get; set; } + + /// + /// Returns the log to write status messages to the console and to a log file. + /// + public ILog Log { get; internal set; } + + /// + /// Returns the directory where the module instances are listed. + /// + private IDictionary Dictionary { get; } + = new Dictionary(); + + /// + /// Returns the associated module contexts. + /// + public IEnumerable ModuleContexts => Dictionary.Keys; + + /// + /// Returns the rest api contexts. + /// + public IEnumerable RestApiContexts => Dictionary.Values; + + /// + /// Initializes a new instance of the class. + /// + /// The rest api manager. + internal RestApiItem(IRestApiManager restApiManager) + { + _restApiManager = restApiManager; + } + + /// + /// Adds an module assignment + /// + /// The context of the module. + public void AddModule(IModuleContext moduleContext) + { + // only if no instance has been created yet + if (Dictionary.ContainsKey(moduleContext)) + { + Log.Warning(message: I18N.Translate("webexpress:restapimanager.addresource.duplicate", RestApiId, moduleContext.ModuleId)); + + return; + } + + // create context + var restApiContext = new RestApiContext(moduleContext, _restApiManager, this) + { + Conditions = Conditions, + EndpointId = RestApiId, + Cache = Cache, + IncludeSubPaths = IncludeSubPaths + }; + + if + ( + !Optional || + moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.{RestApiId.ToLower()}") || + moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.*") || + moduleContext.ApplicationContext.Options.Where(x => Regex.Match($"{ModuleId.ToLower()}.{RestApiId.ToLower()}", x).Success).Any() + ) + { + Dictionary.Add(moduleContext, restApiContext); + OnAddRestApi(restApiContext); + } + } + + /// + /// Remove an module assignment. + /// + /// The context of the module. + public void DetachModule(IModuleContext moduleContext) + { + // not an assignment has been created yet + if (!Dictionary.ContainsKey(moduleContext)) + { + return; + } + + foreach (var restApiContext in Dictionary.Values) + { + OnRemovePage(restApiContext); + } + + Dictionary.Remove(moduleContext); + } + + /// + /// Checks whether a module context is already assigned to the item. + /// + /// The module context. + /// True a mapping exists, false otherwise. + public bool IsAssociatedWithModule(IModuleContext moduleContext) + { + return Dictionary.ContainsKey(moduleContext); + } + + /// + /// Raises the AddRestApi event. + /// + /// The rest api context. + private void OnAddRestApi(IRestApiContext restApiContext) + { + AddRestApi?.Invoke(this, restApiContext); + } + + /// + /// Raises the RemoveRestApi event. + /// + /// The rest api context. + private void OnRemovePage(IRestApiContext restApiContext) + { + RemoveRestApi?.Invoke(this, restApiContext); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged rest api resources. + /// + public void Dispose() + { + foreach (Delegate d in AddRestApi.GetInvocationList()) + { + AddRestApi -= (EventHandler)d; + } + + foreach (Delegate d in RemoveRestApi.GetInvocationList()) + { + RemoveRestApi -= (EventHandler)d; + } + } + + /// + /// Convert the resource element to a string. + /// + /// The rest api resource element in its string representation. + public override string ToString() + { + return $"RestApi '{RestApiId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApi.cs b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs new file mode 100644 index 0000000..c1fba23 --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs @@ -0,0 +1,60 @@ +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// The prototype of a rest api. + /// + public abstract class RestApi : IRestApi + { + /// + /// Returns the rest api context. + /// + public IRestApiContext RestApiContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The context of the rest api resource. + public RestApi(IRestApiContext restApiContext) + { + RestApiContext = restApiContext; + } + + /// + /// Creates data. + /// + /// The request. + public virtual void CreateData(Request request) + { + + } + + /// + /// Gets data. + /// + /// The data. + public virtual object GetData() + { + return null; + } + + /// + /// Updates data. + /// + /// The request. + public virtual void UpdateData(Request request) + { + + } + + /// + /// Deletes data. + /// + /// The request. + public virtual void DeleteData(Request request) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs new file mode 100644 index 0000000..757ef4e --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebRestApi.Model; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// Represents the context of a rest api resource. + /// + public class RestApiContext : IRestApiContext + { + private readonly IRestApiManager _restApiManager; + private readonly RestApiItem _restApiItem; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Returns the corresponding module context. + /// + public IModuleContext ModuleContext { get; private set; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + public IEnumerable Scopes { get; internal set; } + + /// + /// Returns the conditions that must be met for the resource to be active. + /// + public IEnumerable Conditions { get; internal set; } = new List(); + + /// + /// Returns the endpoint id. + /// + public string EndpointId { get; internal set; } + + /// + /// Returns the resource title. + /// + public string PageTitle { get; internal set; } + + /// + /// Returns the parent or null if not used. + /// + public IEndpointContext ParentContext => _restApiManager.RestApis + .Where(x => !string.IsNullOrWhiteSpace(_restApiItem.ParentId)) + .Where(x => x.EndpointId.Equals(_restApiItem.ParentId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) + .FirstOrDefault(); + + /// + /// Returns whether the resource is created once and reused each time it is called. + /// + public bool Cache { get; internal set; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; internal set; } + + /// + /// Returns the context path. + /// + public UriResource ContextPath + { + get + { + var parentContext = ParentContext; + if (parentContext != null) + { + return UriResource.Combine(ParentContext?.Uri, _restApiItem.ContextPath); + } + + return UriResource.Combine(ModuleContext.ContextPath, _restApiItem.ContextPath); + } + } + + /// + /// Returns the uri. + /// + public UriResource Uri => ContextPath.Append(_restApiItem.PathSegment); + + /// + /// Initializes a new instance of the class. + /// + /// The module context. + /// The resource manager. + /// The page item or null. + internal RestApiContext(IModuleContext moduleContext, IRestApiManager restApiManager, RestApiItem restApiItem = null) + { + PluginContext = moduleContext?.PluginContext; + ModuleContext = moduleContext; + _restApiManager = restApiManager; + _restApiItem = restApiItem; + } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs new file mode 100644 index 0000000..b8c6d04 --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -0,0 +1,532 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebRestApi.Model; +using WebExpress.WebCore.WebScope; +using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebStatusPage; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// The rest api manager manages rest api resources, which can be called with a URI (Uniform page Identifier). + /// + public class RestApiManager : IRestApiManager + { + private readonly IComponentHub _componentManager; + private readonly IHttpServerContext _httpServerContext; + private readonly RestApiDictionary _dictionary = []; + + /// + /// An event that fires when an rest api resource is added. + /// + public event EventHandler AddRestApi; + + /// + /// An event that fires when an rest api resource is removed. + /// + public event EventHandler RemoveRestApi; + + /// + /// Returns all rest api resource contexts. + /// + public IEnumerable RestApis => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts); + + /// + /// Initializes a new instance of the class. + /// + /// The component manager. + /// The reference to the context of the host. + private RestApiManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + { + _componentManager = componentManager; + + _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + { + Register(pluginContext); + }; + + _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + { + Remove(pluginContext); + }; + + _componentManager.ModuleManager.AddModule += (sender, moduleContext) => + { + AssignToModule(moduleContext); + }; + + _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => + { + DetachFromModule(moduleContext); + }; + + _componentManager.SitemapManager.Register + ( + new EndpointRegistration() + { + Factory = (resourceContext, uri, culture) => CreatePageInstance(resourceContext as IRestApiContext, culture), + ContextResolver = (type, moduleContext) => moduleContext != null ? GetRestApi(type, moduleContext) : GetRestApi(type), + EndpointResolver = () => RestApis, + HandleRequest = (endpoint, endpontContext, request) => + { + return new ResponseOK() + { + + }; + } + } + ); + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress:restapimanager.initialization") + ); + } + + /// + /// Returns an enumeration of all containing page contexts of a plugin. + /// + /// A context of a plugin whose pages are to be registered. + /// An enumeration of rest api resource contexts. + public IEnumerable GetRestApi(IPluginContext pluginContext) + { + if (!_dictionary.ContainsKey(pluginContext)) + { + return []; + } + + return _dictionary[pluginContext].Values + .SelectMany(x => x.RestApiContexts); + } + + /// + /// Returns an enumeration of rest api resource contextes. + /// + /// The rest api resource type. + /// An enumeration of rest api resource contextes. + public IEnumerable GetRestApi() where T : IRestApi + { + return GetRestApi(typeof(T)); + } + + /// + /// Returns an enumeration of rest api resource contextes. + /// + /// The rest api resource type. + /// An enumeration of rest api resource contextes. + public IEnumerable GetRestApi(Type restApiType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .Where(x => x.RestApiClass.Equals(restApiType)) + .Select(x => x.RestApiContext); + } + + /// + /// Returns an enumeration of rest api resource contextes. + /// + /// The page type. + /// The context of the module. + /// An enumeration of page contextes. + public IEnumerable GetRestApi(Type restApiType, IModuleContext moduleContext) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiClass.Equals(restApiType)) + .Select(x => x.RestApiContext); + } + + /// + /// Returns an enumeration of rest api resource contextes. + /// + /// The rest api resource type. + /// The context of the module. + /// An enumeration of rest api resource contextes. + public IEnumerable GetRestApi(IModuleContext moduleContext) where T : IRestApi + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiClass.Equals(typeof(T))) + .Select(x => x.RestApiContext); + } + + /// + /// Returns the rest api resource context. + /// + /// The context of the module. + /// The rest api resource id. + /// An rest api resource context or null. + public IRestApiContext GetRestApi(IModuleContext moduleContext, string restApiId) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .Where(x => x.RestApiContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.RestApiContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiContext.EndpointId.Equals(restApiId, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.RestApiContext) + .FirstOrDefault(); + } + + /// + /// Returns the rest api resource context. + /// + /// The context of the module. + /// The rest api resource type. + /// An rest api resource context or null. + public IRestApiContext GetRestApi(IModuleContext moduleContext, Type restApiType) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .Where(x => x.RestApiContext.ModuleContext?.ApplicationContext != null) + .Where(x => x.RestApiContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.RestApiClass.Equals(restApiType)) + .Select(x => x.RestApiContext) + .FirstOrDefault(); + } + + /// + /// Returns the rest api resource context. + /// + /// The application id. + /// The module id. + /// The rest api resource id. + /// An rest api resource context or null. + public IRestApiContext GetRestApi(string applicationId, string moduleId, string restApiId) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.RestApiContexts) + .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) + .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => x.EndpointId.Equals(restApiId, StringComparison.OrdinalIgnoreCase)) + .FirstOrDefault(); + } + + /// + /// Creates a new rest api resource and returns it. If a rest api resource already exists (through caching), the existing instance is returned. + /// + /// The context used for rest api resource creation. + /// The culture with the language settings. + /// The created or cached rest api resource. + private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo culture) + { + var resourceItem = _dictionary.Values + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.RestApiContexts.Contains(pageContext)); + + if (resourceItem != null && resourceItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _componentManager); + + if (instance is II18N i18n) + { + i18n.Culture = culture; + } + + if (resourceItem.Cache) + { + resourceItem.Instance = instance; + } + + return instance; + } + + return resourceItem?.Instance as IRestApi; + } + + /// + /// Discovers and registers rest api resources from the specified plugin. + /// + /// A context of a plugin whose rest api resources are to be registered. + public void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + var assembly = pluginContext?.Assembly; + + _dictionary.Add(pluginContext, []); + var dict = _dictionary[pluginContext]; + + foreach (var resourceType in assembly.GetTypes() + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(IRestApi).Name) != null) + .Where(x => x.GetInterface(typeof(IStatusPage).Name) == null)) + { + var id = resourceType.FullName?.ToLower(); + var segment = default(ISegmentAttribute); + var title = resourceType.Name; + var parent = default(string); + var contextPath = string.Empty; + var includeSubPaths = false; + var moduleId = string.Empty; + var scopes = new List(); + var conditions = new List(); + var optional = false; + var cache = false; + + foreach (var customAttribute in resourceType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IResourceAttribute)))) + { + var buf = typeof(ModuleAttribute<>); + + if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) + { + segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; + } + else if (customAttribute.AttributeType == typeof(TitleAttribute)) + { + title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) + { + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + } + else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) + { + contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(IncludeSubPathsAttribute)) + { + includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); + } + else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) + { + moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + } + else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) + { + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + } + else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) + { + var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + conditions.Add(Activator.CreateInstance(condition) as ICondition); + } + else if (customAttribute.AttributeType == typeof(CacheAttribute)) + { + cache = true; + } + else if (customAttribute.AttributeType == typeof(OptionalAttribute)) + { + optional = true; + } + } + + if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) + { + scopes.Add(resourceType.FullName?.ToLower()); + } + + if (string.IsNullOrEmpty(moduleId)) + { + // no module specified + _httpServerContext.Log.Warning + ( + I18N.Translate + ( + "webexpress:restapimanager.moduleless", + id + ) + ); + + continue; + } + + if (!dict.ContainsKey(id)) + { + var restApiItem = new RestApiItem(_componentManager.RestApiManager) + { + RestApiId = id, + ParentId = parent, + RestApiClass = resourceType, + ModuleId = moduleId, + Cache = cache, + Conditions = conditions, + ContextPath = new UriResource(contextPath), + IncludeSubPaths = includeSubPaths, + PathSegment = segment.ToPathSegment(), + Optional = optional, + Log = _httpServerContext?.Log + }; + + restApiItem.AddRestApi += (s, e) => + { + OnAddRestApi(e); + }; + + restApiItem.RemoveRestApi += (s, e) => + { + OnRemoveRestApi(e); + }; + + dict.Add(id, restApiItem); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress:restapimanager.addresource", + id, + moduleId + ) + ); + } + + // assign the rest api resource to existing modules. + foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) + { + AssignToModule(moduleContext); + } + } + } + + /// + /// Discovers and registers rest api resource from the specified plugin. + /// + /// A list with plugin contexts that contain the resources. + public void Register(IEnumerable pluginContexts) + { + foreach (var pluginContext in pluginContexts) + { + Register(pluginContext); + } + } + + /// + /// Removes all pages associated with the specified plugin context. + /// + /// The context of the plugin that contains the rest api resources to remove. + public void Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return; + } + + // the plugin has not been registered in the manager + if (!_dictionary.ContainsKey(pluginContext)) + { + return; + } + + foreach (var resourceItem in _dictionary[pluginContext].Values) + { + resourceItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + } + + /// + /// Assign existing rest api resource to the module. + /// + /// The context of the module. + private void AssignToModule(IModuleContext moduleContext) + { + foreach (var resourceItem in _dictionary.Values + .SelectMany(x => x.Values) + .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) + .Where(x => !x.IsAssociatedWithModule(moduleContext))) + { + resourceItem.AddModule(moduleContext); + } + } + + /// + /// Remove an existing modules to the application. + /// + /// The context of the module. + private void DetachFromModule(IModuleContext moduleContext) + { + foreach (var resourceItem in _dictionary.Values + .SelectMany(x => x.Values) + .Where(x => !x.IsAssociatedWithModule(moduleContext))) + { + resourceItem.DetachModule(moduleContext); + } + } + + /// + /// Returns an enumeration of all containing rest api resource items of a plugin. + /// + /// A context of a plugin whose rest api resources are to be registered. + /// An enumeration of rest api resource items. + private IEnumerable GetPageItems(IPluginContext pluginContext) + { + if (!_dictionary.ContainsKey(pluginContext)) + { + return []; + } + + return _dictionary[pluginContext].Values; + } + + /// + /// Raises the AddRestApi event. + /// + /// The rest api resource context. + private void OnAddRestApi(IRestApiContext resourceContext) + { + AddRestApi?.Invoke(this, resourceContext); + } + + /// + /// Raises the RemoveRestApi event. + /// + /// The rest api resource context. + private void OnRemoveRestApi(IRestApiContext pageContext) + { + RemoveRestApi?.Invoke(this, pageContext); + } + + /// + /// Collects and prepares information about the component for output in the log. + /// + /// The plugin context. + /// A list of log entries. + /// The depth of the log. + public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + { + foreach (var resourcenItem in GetPageItems(pluginContext)) + { + output.Add + ( + string.Empty.PadRight(deep) + + I18N.Translate + ( + "webexpress:restapimanager.resource", + resourcenItem.RestApiId, + string.Join(",", resourcenItem.ModuleId) + ) + ); + } + } + } +} From 0e62392e261770c241f4b999db8fdb44432a8c62 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 8 Oct 2024 23:00:49 +0200 Subject: [PATCH 022/162] improved restapi - support for crud operations - added tests --- .../Fixture/UnitTestControlFixture.cs | 2 +- .../Manager/UnitTestRestApiManager.cs | 19 ++++++ .../Manager/UnitTestSitemapManager.cs | 3 + src/WebExpress.WebCore.Test/TestRestApiA1X.cs | 6 +- src/WebExpress.WebCore.Test/TestRestApiA1Y.cs | 4 +- src/WebExpress.WebCore.Test/TestRestApiA1Z.cs | 25 +++++-- .../Internationalization/de | 1 + .../Internationalization/en | 1 + .../WebAttribute/CacheAttribute.cs | 2 +- .../WebAttribute/ConditionAttribute.cs | 2 +- .../WebAttribute/ContextPathAttribute.cs | 2 +- ...urceAttribute.cs => IEndpointAttribute.cs} | 2 +- .../WebAttribute/IdAttribute.cs | 2 +- .../WebAttribute/IncludeSubPathsAttribute.cs | 2 +- .../WebAttribute/MethodAttribute.cs | 20 ++++++ .../WebAttribute/ModuleAttribute.cs | 2 +- .../WebAttribute/OptionalAttribute.cs | 2 +- .../WebAttribute/ParentAttribute.cs | 2 +- .../WebAttribute/ScopeAttribute.cs | 2 +- .../WebAttribute/SegmentAttribute.cs | 2 +- .../WebAttribute/SegmentDoubleAttribute.cs | 2 +- .../WebAttribute/SegmentGuidAttribute.cs | 2 +- .../WebAttribute/SegmentIntAttribute.cs | 2 +- .../WebAttribute/SegmentStringAttribute.cs | 2 +- .../WebAttribute/SegmentUIntAttribute.cs | 2 +- .../WebAttribute/TitleAttribute.cs | 2 +- .../WebComponent/IEndpointContext.cs | 7 -- .../WebPage/IPageContext.cs | 10 ++- .../WebPage/Model/PageItem.cs | 2 +- src/WebExpress.WebCore/WebPage/PageContext.cs | 4 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 2 +- .../WebResource/Model/ResourceItem.cs | 2 +- .../WebResource/ResourceContext.cs | 7 -- .../WebResource/ResourceManager.cs | 18 +---- .../WebRestAPI/CrudMethod.cs | 30 +++++++++ src/WebExpress.WebCore/WebRestAPI/IRestApi.cs | 3 +- .../WebRestAPI/IRestApiContext.cs | 7 +- .../WebRestAPI/Model/RestApiItem.cs | 10 ++- src/WebExpress.WebCore/WebRestAPI/RestApi.cs | 2 +- .../WebRestAPI/RestApiContext.cs | 10 +-- .../WebRestAPI/RestApiManager.cs | 67 ++++++++++++++----- 41 files changed, 203 insertions(+), 93 deletions(-) rename src/WebExpress.WebCore/WebAttribute/{IResourceAttribute.cs => IEndpointAttribute.cs} (79%) create mode 100644 src/WebExpress.WebCore/WebAttribute/MethodAttribute.cs create mode 100644 src/WebExpress.WebCore/WebRestAPI/CrudMethod.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 58aa0cb..ba5625a 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -133,7 +133,7 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") ["Referer"] = "0HN50661TV8TP" }, Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, - Method = firstLine.Split(' ')?.FirstOrDefault() ?? "GET", + Method = firstLine.Split(' ')?.Where(x => !string.IsNullOrEmpty(x)).FirstOrDefault() ?? "GET", RawTarget = firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.FirstOrDefault() ?? "/", QueryString = "?" + firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.Skip(1)?.FirstOrDefault() ?? "", }; diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index d7c043b..241eaa5 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -76,6 +76,25 @@ public void ContextPath(Type applicationType, Type moduleType, Type resourceType Assert.Equal(id, api.ContextPath); } + /// + /// Test the context path property of the rest api. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), CrudMethod.POST)] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), CrudMethod.GET)] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), CrudMethod.GET)] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), CrudMethod.GET)] + public void Method(Type applicationType, Type moduleType, Type resourceType, CrudMethod method) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + + // test execution + Assert.Contains(method, api.Methods); + } + /// /// Tests whether the rest api manager implements interface IComponentManager. /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index cda6e67..a6f931e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -35,6 +35,9 @@ public void Refresh() [InlineData("http://localhost:8080/aca/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] [InlineData("http://localhost:8080/acb/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] [InlineData("http://localhost:8080/aca/mca/pa1x", "webexpress.webcore.test.testpagea1x")] + [InlineData("http://localhost:8080/aca/mca/ra1x", "webexpress.webcore.test.testrestapia1x")] + [InlineData("http://localhost:8080/aca/mca/ra1x/ra1y", "webexpress.webcore.test.testrestapia1y")] + [InlineData("http://localhost:8080/aca/mca/ra1x/ra1y/ra1z", "webexpress.webcore.test.testrestapia1z")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs index 599606f..e1a054b 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs @@ -8,7 +8,8 @@ namespace WebExpress.WebCore.Test /// [Title("webindex:resourcea1x.label")] [Segment("ra1x", "webindex:homepage.label")] - [ContextPath(null)] + [Method(CrudMethod.POST)] + [Method(CrudMethod.GET)] [Module] public sealed class TestRestApiA1X : IRestApi { @@ -37,8 +38,9 @@ public void CreateData(WebMessage.Request request) /// /// Gets data. /// + /// The request. /// The data. - public object GetData() + public object GetData(WebMessage.Request request) { return null; } diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs index b93b3f4..6fb5925 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs @@ -9,6 +9,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:resourcea1x.label")] [Segment("ra1y", "webindex:homepage.label")] [Parent] + [Method(CrudMethod.GET)] [Module] public sealed class TestRestApiA1Y : IRestApi { @@ -37,8 +38,9 @@ public void CreateData(WebMessage.Request request) /// /// Gets data. /// + /// The request. /// The data. - public object GetData() + public object GetData(WebMessage.Request request) { return null; } diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs index a9a8a86..6c433de 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebRestApi; namespace WebExpress.WebCore.Test @@ -7,17 +8,26 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. /// [Title("webindex:resourcea1x.label")] - [Segment("ra1y", "webindex:homepage.label")] + [Segment("ra1z", "webindex:homepage.label")] [Parent] + [Method(CrudMethod.GET)] [Module] - public sealed class TestRestApiA1Z : IRestApi + public sealed class TestRestApiA1Z : RestApi { /// /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. /// + /// The component hub. /// The context of the restapi resource. - public TestRestApiA1Z(IRestApiContext restApiContext) + public TestRestApiA1Z(IComponentHub componentHub, IRestApiContext restApiContext) + : base(restApiContext) { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + // test the injection if (restApiContext == null) { @@ -29,7 +39,7 @@ public TestRestApiA1Z(IRestApiContext restApiContext) /// Creates data. /// /// The request. - public void CreateData(WebMessage.Request request) + public override void CreateData(WebMessage.Request request) { } @@ -37,8 +47,9 @@ public void CreateData(WebMessage.Request request) /// /// Gets data. /// + /// The request. /// The data. - public object GetData() + public override object GetData(WebMessage.Request request) { return null; } @@ -47,7 +58,7 @@ public object GetData() /// Updates data. /// /// The request. - public void UpdateData(WebMessage.Request request) + public override void UpdateData(WebMessage.Request request) { // test the request if (request == null) @@ -60,7 +71,7 @@ public void UpdateData(WebMessage.Request request) /// Deletes data. /// /// The request. - public void DeleteData(WebMessage.Request request) + public override void DeleteData(WebMessage.Request request) { // test the request if (request == null) diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index b44178b..7b8c5cc 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -112,6 +112,7 @@ restapimanager.moduleless=Die Seite '{0}' besitzt keine Angaben zum Modul. restapimanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. restapimanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. restapimanager.resource=Seite: '{0}' für das Modul '{1}' +restapimanager.methodnotsupported=Die Methode '{0}' wird nicht unterstützt. statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. statuspagemanager.register=Status {0} wurde registriert und der Statusseite '{1}' zugewiesen. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index c0f2aa6..3f3a72d 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -112,6 +112,7 @@ restapimanager.moduleless=The REST API '{0}' does not have any information about restapimanager.addresource=The REST API '{0}' has been registered in the module '{1}'. restapimanager.addresource.duplicate=The REST API '{0}' of module '{1}' has already been added. restapimanager.resource=REST API: '{0}' for module '{1}' +restapimanager.methodnotsupported=The method '{0}' is not supported. statuspagemanager.initialization=The status page manager has been initialized. statuspagemanager.register=Status {0} has been registered and assigned to the status page '{1}'. diff --git a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs index 96348b2..d260ac9 100644 --- a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Indicates that a page or component can be reused /// [AttributeUsage(AttributeTargets.Class)] - public class CacheAttribute : System.Attribute, IResourceAttribute + public class CacheAttribute : System.Attribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs index 27fa603..2d7d46f 100644 --- a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Activation of options (e.g. WebEx.WebApp.Setting.SystemInformation for displaying system information). /// [AttributeUsage(AttributeTargets.Class)] - public class ConditionAttribute : Attribute, IResourceAttribute where T : class, ICondition + public class ConditionAttribute : Attribute, IEndpointAttribute where T : class, ICondition { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs index b1e679a..874e9b6 100644 --- a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class ContextPathAttribute : Attribute, IApplicationAttribute, IModuleAttribute, IResourceAttribute + public class ContextPathAttribute : Attribute, IApplicationAttribute, IModuleAttribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IResourceAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IEndpointAttribute.cs similarity index 79% rename from src/WebExpress.WebCore/WebAttribute/IResourceAttribute.cs rename to src/WebExpress.WebCore/WebAttribute/IEndpointAttribute.cs index e845ac5..7ce2128 100644 --- a/src/WebExpress.WebCore/WebAttribute/IResourceAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IEndpointAttribute.cs @@ -3,7 +3,7 @@ /// /// Interface of a resource assignment attribute. /// - public interface IResourceAttribute + public interface IEndpointAttribute { } } diff --git a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs index 1cbd23a..48703e0 100644 --- a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs @@ -3,7 +3,7 @@ /// /// The unique identification key. /// - public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IResourceAttribute + public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs index 9a93620..f14ea97 100644 --- a/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IncludeSubPathsAttribute.cs @@ -3,7 +3,7 @@ /// /// Determines whether all resources below the specified path (including segment) are also processed. /// - public class IncludeSubPathsAttribute : System.Attribute, IResourceAttribute + public class IncludeSubPathsAttribute : System.Attribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/MethodAttribute.cs b/src/WebExpress.WebCore/WebAttribute/MethodAttribute.cs new file mode 100644 index 0000000..c78a190 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/MethodAttribute.cs @@ -0,0 +1,20 @@ +using System; +using WebExpress.WebCore.WebRestApi; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// The range in which the attribute is valid. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class MethodAttribute : Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + public MethodAttribute(CrudMethod crudMethod) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs index 0c2da75..6486b2b 100644 --- a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// The type of the module. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ModuleAttribute : Attribute, IResourceAttribute, IModuleAttribute where T : class, IModule + public class ModuleAttribute : Attribute, IEndpointAttribute, IModuleAttribute where T : class, IModule { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs index d97abe4..f43dc58 100644 --- a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Marks a ressorce as optional. This becomes active when the option is specified in the application. /// [AttributeUsage(AttributeTargets.Class)] - public class OptionalAttribute : Attribute, IResourceAttribute + public class OptionalAttribute : Attribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs index e8477a9..17faf26 100644 --- a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs @@ -4,7 +4,7 @@ namespace WebExpress.WebCore.WebAttribute { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ParentAttribute : Attribute, IResourceAttribute where T : class, IEndpoint + public class ParentAttribute : Attribute, IEndpointAttribute where T : class, IEndpoint { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs index ccdfd4b..9aff2fd 100644 --- a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// The range in which the component is valid. /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class ScopeAttribute : Attribute, IResourceAttribute where T : class, IScope + public class ScopeAttribute : Attribute, IEndpointAttribute where T : class, IScope { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs index 927a6aa..6c4884d 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// A static path segment. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SegmentAttribute : Attribute, IResourceAttribute, ISegmentAttribute + public class SegmentAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// /// Returns or set the segment of the uri path. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs index 63bcab3..7a04fad 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { - public class SegmentDoubleAttribute : Attribute, IResourceAttribute, ISegmentAttribute + public class SegmentDoubleAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// /// Returns or sets the name of the variable. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs index 8a2a234..7c13d3f 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebAttribute /// A dynamic path segment of type guid. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SegmentGuidAttribute : Attribute, IResourceAttribute, ISegmentAttribute where T : Parameter + public class SegmentGuidAttribute : Attribute, IEndpointAttribute, ISegmentAttribute where T : Parameter { /// /// Returns or sets the name of the variable. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs index 1bc82dd..04dd4d9 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { - public class SegmentIntAttribute : Attribute, IResourceAttribute, ISegmentAttribute + public class SegmentIntAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// /// Returns or sets the name of the variable. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs index 07f5058..9a3d8b7 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { - public class SegmentStringAttribute : Attribute, IResourceAttribute, ISegmentAttribute + public class SegmentStringAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// /// Returns or sets the name of the variable. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs index f379a4a..c8aab42 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { - public class SegmentUIntAttribute : Attribute, IResourceAttribute, ISegmentAttribute + public class SegmentUIntAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// /// Returns or sets the name of the variable. diff --git a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs index 7189ba8..49fbcf4 100644 --- a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class TitleAttribute : System.Attribute, IResourceAttribute + public class TitleAttribute : System.Attribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs b/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs index 1caf936..e43af26 100644 --- a/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs +++ b/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs @@ -26,13 +26,6 @@ public interface IEndpointContext : IContext /// IModuleContext ModuleContext { get; } - /// - /// Returns the scope names that provides the resource. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - IEnumerable Scopes { get; } - /// /// Provides the conditions that must be met for the resource to be active. /// diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index 3de4921..c9df8bc 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebComponent; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPage { @@ -11,5 +12,12 @@ public interface IPageContext : IEndpointContext /// Returns the resource title. /// string PageTitle { get; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + IEnumerable Scopes { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index c005a66..74341e4 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -82,7 +82,7 @@ internal class PageItem : IDisposable /// /// Returns the conditions that must be met for the resource to be active. /// - public ICollection Conditions { get; set; } + public IEnumerable Conditions { get; set; } /// /// Returns whether the resource is created once and reused each time it is called. diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index c181bcc..af2c526 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -33,12 +33,12 @@ public class PageContext : IPageContext /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// - public IEnumerable Scopes { get; internal set; } + public IEnumerable Scopes { get; internal set; } = []; /// /// Returns the conditions that must be met for the resource to be active. /// - public IEnumerable Conditions { get; internal set; } = new List(); + public IEnumerable Conditions { get; internal set; } = []; /// /// Returns the endpoint id. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index be8d2ec..bf6d2f8 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -309,7 +309,7 @@ public void Register(IPluginContext pluginContext) var cache = false; foreach (var customAttribute in resourceType.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IResourceAttribute)))) + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { var buf = typeof(ModuleAttribute<>); diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index 599bc83..d44a5e7 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -83,7 +83,7 @@ internal class ResourceItem : IDisposable /// /// Returns the conditions that must be met for the resource to be active. /// - public ICollection Conditions { get; set; } + public IEnumerable Conditions { get; set; } /// /// Returns whether the resource is created once and reused each time it is called. diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index 4570e60..e16b54b 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -28,13 +28,6 @@ public class ResourceContext : IResourceContext /// public IModuleContext ModuleContext { get; private set; } - /// - /// Returns the scope names that provides the resource. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - public IEnumerable Scopes { get; internal set; } - /// /// Returns the conditions that must be met for the resource to be active. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index cecd2cc..d160f26 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -9,7 +9,6 @@ using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource.Model; -using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; @@ -113,18 +112,16 @@ public void Register(IPluginContext pluginContext) { var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); - var title = resourceType.Name; var parent = default(string); var contextPath = string.Empty; var includeSubPaths = false; var moduleId = string.Empty; - var scopes = new List(); var conditions = new List(); var optional = false; var cache = false; foreach (var customAttribute in resourceType.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IResourceAttribute)))) + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { var buf = typeof(ModuleAttribute<>); @@ -132,10 +129,6 @@ public void Register(IPluginContext pluginContext) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } - else if (customAttribute.AttributeType == typeof(TitleAttribute)) - { - title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); @@ -152,10 +145,6 @@ public void Register(IPluginContext pluginContext) { moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); } - else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) - { - scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -171,11 +160,6 @@ public void Register(IPluginContext pluginContext) } } - if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) - { - scopes.Add(resourceType.FullName?.ToLower()); - } - if (string.IsNullOrEmpty(moduleId)) { // no module specified diff --git a/src/WebExpress.WebCore/WebRestAPI/CrudMethod.cs b/src/WebExpress.WebCore/WebRestAPI/CrudMethod.cs new file mode 100644 index 0000000..33f66be --- /dev/null +++ b/src/WebExpress.WebCore/WebRestAPI/CrudMethod.cs @@ -0,0 +1,30 @@ +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebRestApi +{ + /// + /// Represents the crud methods for a rest api. + /// + public enum CrudMethod + { + /// + /// Represents the HTTP POST method. + /// + POST = RequestMethod.POST, + + /// + /// Represents the HTTP GET method. + /// + GET = RequestMethod.GET, + + /// + /// Represents the HTTP PATCH method. + /// + PATCH = RequestMethod.PATCH, + + /// + /// Represents the HTTP DELETE method. + /// + DELETE = RequestMethod.DELETE + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs index a813cf5..881c4b8 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs @@ -17,8 +17,9 @@ public interface IRestApi : IEndpoint /// /// Gets data. /// + /// The request. /// The data. - object GetData(); + object GetData(Request request); /// /// Updates data. diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs index d3f7d3a..68aa0dc 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebComponent; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebRestApi { @@ -7,5 +8,9 @@ namespace WebExpress.WebCore.WebRestApi /// public interface IRestApiContext : IEndpointContext { + /// + /// Returns the crud methods. + /// + IEnumerable Methods { get; } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs index ed913da..b748920 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -70,7 +70,12 @@ internal class RestApiItem : IDisposable /// /// Returns the conditions that must be met for the rest api resource to be active. /// - public ICollection Conditions { get; set; } + public IEnumerable Conditions { get; set; } + + /// + /// Returns the crud methods. + /// + public IEnumerable Methods { get; set; } /// /// Returns whether the resource is created once and reused each time it is called. @@ -132,7 +137,8 @@ public void AddModule(IModuleContext moduleContext) Conditions = Conditions, EndpointId = RestApiId, Cache = Cache, - IncludeSubPaths = IncludeSubPaths + IncludeSubPaths = IncludeSubPaths, + Methods = Methods }; if diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApi.cs b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs index c1fba23..45dda27 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApi.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs @@ -34,7 +34,7 @@ public virtual void CreateData(Request request) /// Gets data. /// /// The data. - public virtual object GetData() + public virtual object GetData(Request request) { return null; } diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs index 757ef4e..566eadd 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs @@ -38,17 +38,17 @@ public class RestApiContext : IRestApiContext /// /// Returns the conditions that must be met for the resource to be active. /// - public IEnumerable Conditions { get; internal set; } = new List(); + public IEnumerable Conditions { get; internal set; } = []; /// - /// Returns the endpoint id. + /// Returns the crud methods. /// - public string EndpointId { get; internal set; } + public IEnumerable Methods { get; internal set; } = []; /// - /// Returns the resource title. + /// Returns the endpoint id. /// - public string PageTitle { get; internal set; } + public string EndpointId { get; internal set; } /// /// Returns the parent or null if not used. diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index b8c6d04..22d53fb 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text; +using System.Text.Json; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; @@ -10,7 +12,6 @@ using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebRestApi.Model; -using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; @@ -25,6 +26,7 @@ public class RestApiManager : IRestApiManager private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly RestApiDictionary _dictionary = []; + private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true }; /// /// An event that fires when an rest api resource is added. @@ -81,9 +83,45 @@ private RestApiManager(IComponentHub componentManager, IHttpServerContext httpSe EndpointResolver = () => RestApis, HandleRequest = (endpoint, endpontContext, request) => { - return new ResponseOK() - { + var restApi = endpoint as IRestApi; + var restApiContext = endpontContext as IRestApiContext; + if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) + { + switch (request.Method) + { + case RequestMethod.POST: + restApi.CreateData(request); + + return new ResponseOK(); + case RequestMethod.GET: + var data = restApi.GetData(request); + if (data != null) + { + var jsonData = JsonSerializer.Serialize(data, _jsonOptions); + var content = Encoding.UTF8.GetBytes(jsonData); + + return new ResponseOK + { + Content = content + }; + } + + return new ResponseOK(); + case RequestMethod.PATCH: + restApi.UpdateData(request); + + return new ResponseOK(); + case RequestMethod.DELETE: + restApi.DeleteData(request); + + return new ResponseOK(); + } + } + + return new ResponseBadRequest() + { + Content = I18N.Translate("webexpress:restapimanager.methodnotsupported", request.Method.ToString()) }; } } @@ -286,13 +324,13 @@ public void Register(IPluginContext pluginContext) var contextPath = string.Empty; var includeSubPaths = false; var moduleId = string.Empty; - var scopes = new List(); var conditions = new List(); var optional = false; var cache = false; + var methods = new List(); foreach (var customAttribute in resourceType.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IResourceAttribute)))) + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { var buf = typeof(ModuleAttribute<>); @@ -300,10 +338,6 @@ public void Register(IPluginContext pluginContext) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } - else if (customAttribute.AttributeType == typeof(TitleAttribute)) - { - title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); @@ -320,15 +354,16 @@ public void Register(IPluginContext pluginContext) { moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); } - else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) - { - scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); conditions.Add(Activator.CreateInstance(condition) as ICondition); } + else if (customAttribute.AttributeType.Name == typeof(MethodAttribute).Name && customAttribute.AttributeType.Namespace == typeof(MethodAttribute).Namespace) + { + var method = (CrudMethod)customAttribute.ConstructorArguments.FirstOrDefault().Value; + methods.Add(method); + } else if (customAttribute.AttributeType == typeof(CacheAttribute)) { cache = true; @@ -339,11 +374,6 @@ public void Register(IPluginContext pluginContext) } } - if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) - { - scopes.Add(resourceType.FullName?.ToLower()); - } - if (string.IsNullOrEmpty(moduleId)) { // no module specified @@ -367,6 +397,7 @@ public void Register(IPluginContext pluginContext) ParentId = parent, RestApiClass = resourceType, ModuleId = moduleId, + Methods = methods.Distinct(), Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), From c7aa50887f0ac19de371624fe03c443f85564fd4 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 8 Oct 2024 23:27:42 +0200 Subject: [PATCH 023/162] added versioning to restapi --- .../Manager/UnitTestRestApiManager.cs | 6 +++--- .../Manager/UnitTestSitemapManager.cs | 8 ++++---- src/WebExpress.WebCore.Test/TestRestApiA1X.cs | 1 + src/WebExpress.WebCore.Test/TestRestApiA1Y.cs | 1 + src/WebExpress.WebCore.Test/TestRestApiA1Z.cs | 1 + .../WebAttribute/VersionAttribute.cs | 20 +++++++++++++++++++ .../WebRestAPI/IRestApiContext.cs | 5 +++++ .../WebRestAPI/Model/RestApiItem.cs | 8 +++++++- .../WebRestAPI/RestApiContext.cs | 9 +++++++-- .../WebRestAPI/RestApiManager.cs | 6 ++++++ 10 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 241eaa5..ef37257 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -62,9 +62,9 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string /// Test the context path property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "/aca/mca/ra1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "/aca/mca/ra1x/ra1y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "/aca/mca/1")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "/aca/mca/1/ra1x/2")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "/aca/mca/1/ra1x/2/ra1y/3")] public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index a6f931e..d2e07a8 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,7 +22,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(19, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(22, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -35,9 +35,9 @@ public void Refresh() [InlineData("http://localhost:8080/aca/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] [InlineData("http://localhost:8080/acb/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] [InlineData("http://localhost:8080/aca/mca/pa1x", "webexpress.webcore.test.testpagea1x")] - [InlineData("http://localhost:8080/aca/mca/ra1x", "webexpress.webcore.test.testrestapia1x")] - [InlineData("http://localhost:8080/aca/mca/ra1x/ra1y", "webexpress.webcore.test.testrestapia1y")] - [InlineData("http://localhost:8080/aca/mca/ra1x/ra1y/ra1z", "webexpress.webcore.test.testrestapia1z")] + [InlineData("http://localhost:8080/aca/mca/1/ra1x", "webexpress.webcore.test.testrestapia1x")] + [InlineData("http://localhost:8080/aca/mca/1/ra1x/2/ra1y", "webexpress.webcore.test.testrestapia1y")] + [InlineData("http://localhost:8080/aca/mca/1/ra1x/2/ra1y/3/ra1z", "webexpress.webcore.test.testrestapia1z")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs index e1a054b..b59abfc 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs @@ -10,6 +10,7 @@ namespace WebExpress.WebCore.Test [Segment("ra1x", "webindex:homepage.label")] [Method(CrudMethod.POST)] [Method(CrudMethod.GET)] + [Version(1)] [Module] public sealed class TestRestApiA1X : IRestApi { diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs index 6fb5925..a6f1086 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs @@ -10,6 +10,7 @@ namespace WebExpress.WebCore.Test [Segment("ra1y", "webindex:homepage.label")] [Parent] [Method(CrudMethod.GET)] + [Version(2)] [Module] public sealed class TestRestApiA1Y : IRestApi { diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs index 6c433de..974279d 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs @@ -11,6 +11,7 @@ namespace WebExpress.WebCore.Test [Segment("ra1z", "webindex:homepage.label")] [Parent] [Method(CrudMethod.GET)] + [Version(3)] [Module] public sealed class TestRestApiA1Z : RestApi { diff --git a/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs new file mode 100644 index 0000000..dc41195 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Specifying a version for a rest api. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class VersionAttribute : Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The version number of the rest api. + public VersionAttribute(uint version) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs index 68aa0dc..359a1f2 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs @@ -12,5 +12,10 @@ public interface IRestApiContext : IEndpointContext /// Returns the crud methods. /// IEnumerable Methods { get; } + + /// + /// Returns the version number of the rest api. + /// + uint Version { get; } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs index b748920..ac931be 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -77,6 +77,11 @@ internal class RestApiItem : IDisposable /// public IEnumerable Methods { get; set; } + /// + /// Returns the version number of the rest api. + /// + public uint Version { get; set; } + /// /// Returns whether the resource is created once and reused each time it is called. /// @@ -138,7 +143,8 @@ public void AddModule(IModuleContext moduleContext) EndpointId = RestApiId, Cache = Cache, IncludeSubPaths = IncludeSubPaths, - Methods = Methods + Methods = Methods, + Version = Version }; if diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs index 566eadd..6a70d5c 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs @@ -59,6 +59,11 @@ public class RestApiContext : IRestApiContext .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) .FirstOrDefault(); + /// + /// Returns the version number of the rest api. + /// + public uint Version { get; internal set; } + /// /// Returns whether the resource is created once and reused each time it is called. /// @@ -79,10 +84,10 @@ public UriResource ContextPath var parentContext = ParentContext; if (parentContext != null) { - return UriResource.Combine(ParentContext?.Uri, _restApiItem.ContextPath); + return UriResource.Combine(ParentContext?.Uri, _restApiItem.ContextPath, Version.ToString()); } - return UriResource.Combine(ModuleContext.ContextPath, _restApiItem.ContextPath); + return UriResource.Combine(ModuleContext.ContextPath, _restApiItem.ContextPath, Version.ToString()); } } diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 22d53fb..ade3fd0 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -328,6 +328,7 @@ public void Register(IPluginContext pluginContext) var optional = false; var cache = false; var methods = new List(); + var version = 1u; foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) @@ -364,6 +365,10 @@ public void Register(IPluginContext pluginContext) var method = (CrudMethod)customAttribute.ConstructorArguments.FirstOrDefault().Value; methods.Add(method); } + else if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) + { + version = Convert.ToUInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); + } else if (customAttribute.AttributeType == typeof(CacheAttribute)) { cache = true; @@ -398,6 +403,7 @@ public void Register(IPluginContext pluginContext) RestApiClass = resourceType, ModuleId = moduleId, Methods = methods.Distinct(), + Version = version, Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), From c108f2b1474d3c4f1b42dc63238b2ecbb5c63a21 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 9 Oct 2024 02:28:25 +0200 Subject: [PATCH 024/162] refactoring --- .../InternationalizationManager.cs | 1 + .../InternationalizationDictionary.cs | 2 +- .../{ => Model}/InternationalizationItem.cs | 2 +- .../WebApplication/ApplicationManager.cs | 1 + .../{ => Model}/ApplicationDictionary.cs | 2 +- .../{ => Model}/ApplicationItem.cs | 2 +- .../WebComponent/ComponentHub.cs | 1 + .../{ => Model}/ComponentDictionary.cs | 4 ++-- .../WebComponent/{ => Model}/ComponentItem.cs | 2 +- .../WebEvent/EventManager.cs | 2 +- .../WebEvent/{ => Model}/EventDictionary.cs | 2 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 1 + .../WebJob/{ => Model}/ScheduleDictionary.cs | 2 +- .../{ => Model}/ScheduleIDynamicItem.cs | 2 +- .../WebJob/{ => Model}/ScheduleStaticItem.cs | 2 +- .../{ => Model}/ScheduleStaticItemValue.cs | 2 +- src/WebExpress.WebCore/WebMessage/Request.cs | 2 +- .../WebModule/{ => Model}/ModuleDictionary.cs | 2 +- .../WebModule/{ => Model}/ModuleItem.cs | 2 +- .../WebModule/ModuleManager.cs | 1 + .../WebPackage/{ => Model}/PackageCatalog.cs | 4 ++-- .../{ => Model}/PackageCatalogItem.cs | 2 +- .../{ => Model}/PackageCatalogeItemState.cs | 2 +- .../WebPackage/{ => Model}/PackageItem.cs | 2 +- .../WebPackage/{ => Model}/PackageItemSpec.cs | 2 +- .../WebPackage/PackageBuilder.cs | 1 + .../WebPackage/PackageManager.cs | 1 + .../WebPlugin/{ => Model}/PluginDictionary.cs | 2 +- .../WebPlugin/{ => Model}/PluginItem.cs | 2 +- .../{ => Model}/PluginLoadContext.cs | 9 ++++----- .../WebPlugin/PluginManager.cs | 1 + .../WebSession/AuthorizationService.cs | 4 +++- .../WebSession/{ => Model}/Session.cs | 2 +- .../{ => Model}/SessionDictionary.cs | 2 +- .../WebSession/{ => Model}/SessionProperty.cs | 2 +- .../SessionPropertyAuthentification.cs | 2 +- .../SessionPropertyAuthorization.cs | 2 +- .../{ => Model}/SessionPropertyParameter.cs | 2 +- .../WebSession/SessionManager.cs | 1 + .../WebSitemap/{ => Model}/SitemapNode.cs | 2 +- .../WebSitemap/SearchResult.cs | 1 + .../WebSitemap/SitemapManager.cs | 1 + .../{ => Model}/StatusPageDictionary.cs | 4 ++-- .../{ => Model}/StatusPageDictionaryItem.cs | 4 ++-- .../{ => Model}/StatusPageItem.cs | 4 ++-- .../WebStatusPage/StatusPageManager.cs | 1 + .../WebTask/{ => Model}/TaskDictionary.cs | 2 +- src/WebExpress.WebCore/WebTask/Task.cs | 19 ++++++++++--------- src/WebExpress.WebCore/WebTask/TaskManager.cs | 1 + 49 files changed, 68 insertions(+), 53 deletions(-) rename src/WebExpress.WebCore/Internationalization/{ => Model}/InternationalizationDictionary.cs (82%) rename src/WebExpress.WebCore/Internationalization/{ => Model}/InternationalizationItem.cs (69%) rename src/WebExpress.WebCore/WebApplication/{ => Model}/ApplicationDictionary.cs (86%) rename src/WebExpress.WebCore/WebApplication/{ => Model}/ApplicationItem.cs (94%) rename src/WebExpress.WebCore/WebComponent/{ => Model}/ComponentDictionary.cs (61%) rename src/WebExpress.WebCore/WebComponent/{ => Model}/ComponentItem.cs (93%) rename src/WebExpress.WebCore/WebEvent/{ => Model}/EventDictionary.cs (87%) rename src/WebExpress.WebCore/WebJob/{ => Model}/ScheduleDictionary.cs (87%) rename src/WebExpress.WebCore/WebJob/{ => Model}/ScheduleIDynamicItem.cs (94%) rename src/WebExpress.WebCore/WebJob/{ => Model}/ScheduleStaticItem.cs (99%) rename src/WebExpress.WebCore/WebJob/{ => Model}/ScheduleStaticItemValue.cs (94%) rename src/WebExpress.WebCore/WebModule/{ => Model}/ModuleDictionary.cs (87%) rename src/WebExpress.WebCore/WebModule/{ => Model}/ModuleItem.cs (99%) rename src/WebExpress.WebCore/WebPackage/{ => Model}/PackageCatalog.cs (91%) rename src/WebExpress.WebCore/WebPackage/{ => Model}/PackageCatalogItem.cs (96%) rename src/WebExpress.WebCore/WebPackage/{ => Model}/PackageCatalogeItemState.cs (90%) rename src/WebExpress.WebCore/WebPackage/{ => Model}/PackageItem.cs (97%) rename src/WebExpress.WebCore/WebPackage/{ => Model}/PackageItemSpec.cs (98%) rename src/WebExpress.WebCore/WebPlugin/{ => Model}/PluginDictionary.cs (85%) rename src/WebExpress.WebCore/WebPlugin/{ => Model}/PluginItem.cs (96%) rename src/WebExpress.WebCore/WebPlugin/{ => Model}/PluginLoadContext.cs (91%) rename src/WebExpress.WebCore/WebSession/{ => Model}/Session.cs (98%) rename src/WebExpress.WebCore/WebSession/{ => Model}/SessionDictionary.cs (85%) rename src/WebExpress.WebCore/WebSession/{ => Model}/SessionProperty.cs (78%) rename src/WebExpress.WebCore/WebSession/{ => Model}/SessionPropertyAuthentification.cs (87%) rename src/WebExpress.WebCore/WebSession/{ => Model}/SessionPropertyAuthorization.cs (62%) rename src/WebExpress.WebCore/WebSession/{ => Model}/SessionPropertyParameter.cs (94%) rename src/WebExpress.WebCore/WebSitemap/{ => Model}/SitemapNode.cs (98%) rename src/WebExpress.WebCore/WebStatusPage/{ => Model}/StatusPageDictionary.cs (57%) rename src/WebExpress.WebCore/WebStatusPage/{ => Model}/StatusPageDictionaryItem.cs (53%) rename src/WebExpress.WebCore/WebStatusPage/{ => Model}/StatusPageItem.cs (93%) rename src/WebExpress.WebCore/WebTask/{ => Model}/TaskDictionary.cs (85%) diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index fe264e5..9d7e28f 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Reflection; +using WebExpress.WebCore.Internationalization.Model; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationDictionary.cs b/src/WebExpress.WebCore/Internationalization/Model/InternationalizationDictionary.cs similarity index 82% rename from src/WebExpress.WebCore/Internationalization/InternationalizationDictionary.cs rename to src/WebExpress.WebCore/Internationalization/Model/InternationalizationDictionary.cs index de064ec..ac4087b 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationDictionary.cs +++ b/src/WebExpress.WebCore/Internationalization/Model/InternationalizationDictionary.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.Internationalization +namespace WebExpress.WebCore.Internationalization.Model { /// /// key = language (ISO 639-1 two-letter) diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationItem.cs b/src/WebExpress.WebCore/Internationalization/Model/InternationalizationItem.cs similarity index 69% rename from src/WebExpress.WebCore/Internationalization/InternationalizationItem.cs rename to src/WebExpress.WebCore/Internationalization/Model/InternationalizationItem.cs index 9aaa614..40ede17 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationItem.cs +++ b/src/WebExpress.WebCore/Internationalization/Model/InternationalizationItem.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.Internationalization +namespace WebExpress.WebCore.Internationalization.Model { internal class InternationalizationItem : Dictionary { diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 12918a0..205140a 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication.Model; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationDictionary.cs b/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs similarity index 86% rename from src/WebExpress.WebCore/WebApplication/ApplicationDictionary.cs rename to src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs index b6d1622..93e6801 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationDictionary.cs +++ b/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebApplication +namespace WebExpress.WebCore.WebApplication.Model { /// /// Key = Plugin context diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs b/src/WebExpress.WebCore/WebApplication/Model/ApplicationItem.cs similarity index 94% rename from src/WebExpress.WebCore/WebApplication/ApplicationItem.cs rename to src/WebExpress.WebCore/WebApplication/Model/ApplicationItem.cs index e148cc0..2ed8b64 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationItem.cs +++ b/src/WebExpress.WebCore/WebApplication/Model/ApplicationItem.cs @@ -1,7 +1,7 @@ using System; using System.Threading; -namespace WebExpress.WebCore.WebApplication +namespace WebExpress.WebCore.WebApplication.Model { /// /// Represents an application entry in the application directory. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index a87c6fe..4e34f84 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -3,6 +3,7 @@ using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent.Model; using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; diff --git a/src/WebExpress.WebCore/WebComponent/ComponentDictionary.cs b/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs similarity index 61% rename from src/WebExpress.WebCore/WebComponent/ComponentDictionary.cs rename to src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs index 57ff804..99ee85e 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentDictionary.cs +++ b/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs @@ -1,14 +1,14 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebComponent.Model { /// /// Internal management of components. /// key = plugin /// value = component item /// - public class ComponentDictionary : Dictionary> + internal class ComponentDictionary : Dictionary> { } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs b/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs similarity index 93% rename from src/WebExpress.WebCore/WebComponent/ComponentItem.cs rename to src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs index 5a7aada..1c85b30 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentItem.cs +++ b/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs @@ -1,6 +1,6 @@ using System; -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebComponent.Model { public class ComponentItem { diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 09a6363..e665d10 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -2,7 +2,7 @@ using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebJob; +using WebExpress.WebCore.WebEvent.Model; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/WebEvent/EventDictionary.cs b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs similarity index 87% rename from src/WebExpress.WebCore/WebEvent/EventDictionary.cs rename to src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs index a6bf724..d488345 100644 --- a/src/WebExpress.WebCore/WebEvent/EventDictionary.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs @@ -2,7 +2,7 @@ using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebEvent.Model { /// /// key = plugin context diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 7b80adb..1d52a8b 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -6,6 +6,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebJob.Model; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/WebJob/ScheduleDictionary.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs similarity index 87% rename from src/WebExpress.WebCore/WebJob/ScheduleDictionary.cs rename to src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs index f84026b..d0dd095 100644 --- a/src/WebExpress.WebCore/WebJob/ScheduleDictionary.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebJob.Model { /// /// key = plugin context diff --git a/src/WebExpress.WebCore/WebJob/ScheduleIDynamicItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs similarity index 94% rename from src/WebExpress.WebCore/WebJob/ScheduleIDynamicItem.cs rename to src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs index 6594766..13e5392 100644 --- a/src/WebExpress.WebCore/WebJob/ScheduleIDynamicItem.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs @@ -1,6 +1,6 @@ using System.Threading; -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebJob.Model { /// /// Represents an job entry in the dynamic job execution list. diff --git a/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs similarity index 99% rename from src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs rename to src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs index ee86178..56bf97a 100644 --- a/src/WebExpress.WebCore/WebJob/ScheduleStaticItem.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs @@ -5,7 +5,7 @@ using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebJob.Model { /// /// Represents an appointment entry in the appointment execution directory diff --git a/src/WebExpress.WebCore/WebJob/ScheduleStaticItemValue.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs similarity index 94% rename from src/WebExpress.WebCore/WebJob/ScheduleStaticItemValue.cs rename to src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs index 8017517..d9bb65d 100644 --- a/src/WebExpress.WebCore/WebJob/ScheduleStaticItemValue.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs @@ -1,6 +1,6 @@ using System.Threading; -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebJob.Model { /// /// Represents an job entry in the job execution directory. diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index c2dc6f3..0bbfb6a 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -9,7 +9,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using WebExpress.WebCore.WebHtml; -using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSession.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebMessage diff --git a/src/WebExpress.WebCore/WebModule/ModuleDictionary.cs b/src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs similarity index 87% rename from src/WebExpress.WebCore/WebModule/ModuleDictionary.cs rename to src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs index 33573e8..dc52168 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleDictionary.cs +++ b/src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebModule +namespace WebExpress.WebCore.WebModule.Model { /// /// Key = plugin context diff --git a/src/WebExpress.WebCore/WebModule/ModuleItem.cs b/src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs similarity index 99% rename from src/WebExpress.WebCore/WebModule/ModuleItem.cs rename to src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs index 792d07d..c0d06b3 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleItem.cs +++ b/src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs @@ -11,7 +11,7 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebModule +namespace WebExpress.WebCore.WebModule.Model { /// /// Represents a module entry in the module directory. diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index cce6a87..77a11a9 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -5,6 +5,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebModule.Model; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; diff --git a/src/WebExpress.WebCore/WebPackage/PackageCatalog.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs similarity index 91% rename from src/WebExpress.WebCore/WebPackage/PackageCatalog.cs rename to src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs index dded64f..9ab98ab 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageCatalog.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Xml.Serialization; -namespace WebExpress.WebCore.WebPackage +namespace WebExpress.WebCore.WebPackage.Model { [XmlRoot("catalog")] public class PackageCatalog @@ -25,7 +25,7 @@ public class PackageCatalog /// /// The package id. /// The catalog item or null. - public PackageCatalogItem Find(string id) + public PackageCatalogItem Find(string id) { return Packages .Where(x => x.Id.Equals(id, StringComparison.OrdinalIgnoreCase)) diff --git a/src/WebExpress.WebCore/WebPackage/PackageCatalogItem.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs similarity index 96% rename from src/WebExpress.WebCore/WebPackage/PackageCatalogItem.cs rename to src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs index c47daf0..b87f156 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageCatalogItem.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs @@ -2,7 +2,7 @@ using System.Xml.Serialization; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebPackage +namespace WebExpress.WebCore.WebPackage.Model { [XmlRoot("package")] public class PackageCatalogItem diff --git a/src/WebExpress.WebCore/WebPackage/PackageCatalogeItemState.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs similarity index 90% rename from src/WebExpress.WebCore/WebPackage/PackageCatalogeItemState.cs rename to src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs index 34ce0b7..46d1725 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageCatalogeItemState.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs @@ -1,4 +1,4 @@ -namespace WebExpress.WebCore.WebPackage +namespace WebExpress.WebCore.WebPackage.Model { public enum PackageCatalogeItemState { diff --git a/src/WebExpress.WebCore/WebPackage/PackageItem.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs similarity index 97% rename from src/WebExpress.WebCore/WebPackage/PackageItem.cs rename to src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs index 7090cbc..846c6b8 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageItem.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.WebPackage +namespace WebExpress.WebCore.WebPackage.Model { public class PackageItem { diff --git a/src/WebExpress.WebCore/WebPackage/PackageItemSpec.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageItemSpec.cs similarity index 98% rename from src/WebExpress.WebCore/WebPackage/PackageItemSpec.cs rename to src/WebExpress.WebCore/WebPackage/Model/PackageItemSpec.cs index c2762df..aad4181 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageItemSpec.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageItemSpec.cs @@ -1,6 +1,6 @@ using System.Xml.Serialization; -namespace WebExpress.WebCore.WebPackage +namespace WebExpress.WebCore.WebPackage.Model { /// /// The package specification is an XML file containing the specification of the package. diff --git a/src/WebExpress.WebCore/WebPackage/PackageBuilder.cs b/src/WebExpress.WebCore/WebPackage/PackageBuilder.cs index e77b905..e601d24 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageBuilder.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageBuilder.cs @@ -3,6 +3,7 @@ using System.IO.Compression; using System.Linq; using System.Xml.Serialization; +using WebExpress.WebCore.WebPackage.Model; namespace WebExpress.WebCore.WebPackage { diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index c01a9dc..6570d0d 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -12,6 +12,7 @@ using System.Xml.Serialization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPackage.Model; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebPackage diff --git a/src/WebExpress.WebCore/WebPlugin/PluginDictionary.cs b/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs similarity index 85% rename from src/WebExpress.WebCore/WebPlugin/PluginDictionary.cs rename to src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs index c794848..f91e6f7 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginDictionary.cs +++ b/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.WebPlugin +namespace WebExpress.WebCore.WebPlugin.Model { /// /// Verzeichnis über die registrieten Plugins diff --git a/src/WebExpress.WebCore/WebPlugin/PluginItem.cs b/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs similarity index 96% rename from src/WebExpress.WebCore/WebPlugin/PluginItem.cs rename to src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs index 775eb71..f9f9a01 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginItem.cs +++ b/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Threading; -namespace WebExpress.WebCore.WebPlugin +namespace WebExpress.WebCore.WebPlugin.Model { /// /// Represents a plugin entry. diff --git a/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs b/src/WebExpress.WebCore/WebPlugin/Model/PluginLoadContext.cs similarity index 91% rename from src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs rename to src/WebExpress.WebCore/WebPlugin/Model/PluginLoadContext.cs index 8a95b29..62499f8 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginLoadContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/Model/PluginLoadContext.cs @@ -1,8 +1,7 @@ -using System; -using System.Reflection; +using System.Reflection; using System.Runtime.Loader; -namespace WebExpress.WebCore.WebPlugin +namespace WebExpress.WebCore.WebPlugin.Model { /// /// Isolation of plug-in dependencies. @@ -46,7 +45,7 @@ protected override Assembly Load(AssemblyName assemblyName) /// /// Name of the unmanaged library. Typically this is the filename without its path or extensions. /// A handle to the loaded library, or Zero. - protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) + protected override nint LoadUnmanagedDll(string unmanagedDllName) { string libraryPath = Resolver.ResolveUnmanagedDllToPath(unmanagedDllName); @@ -55,7 +54,7 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) return LoadUnmanagedDllFromPath(libraryPath); } - return IntPtr.Zero; + return nint.Zero; } } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 2093821..2835299 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebPlugin.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPlugin diff --git a/src/WebExpress.WebCore/WebSession/AuthorizationService.cs b/src/WebExpress.WebCore/WebSession/AuthorizationService.cs index 8654c7b..509d8b8 100644 --- a/src/WebExpress.WebCore/WebSession/AuthorizationService.cs +++ b/src/WebExpress.WebCore/WebSession/AuthorizationService.cs @@ -1,4 +1,6 @@ -namespace WebExpress.WebCore.WebSession +using WebExpress.WebCore.WebSession.Model; + +namespace WebExpress.WebCore.WebSession { public abstract class AuthorizationService { diff --git a/src/WebExpress.WebCore/WebSession/Session.cs b/src/WebExpress.WebCore/WebSession/Model/Session.cs similarity index 98% rename from src/WebExpress.WebCore/WebSession/Session.cs rename to src/WebExpress.WebCore/WebSession/Model/Session.cs index e3e1676..300d186 100644 --- a/src/WebExpress.WebCore/WebSession/Session.cs +++ b/src/WebExpress.WebCore/WebSession/Model/Session.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { /// /// Represents a session.Through a session, session data can be assigned to diff --git a/src/WebExpress.WebCore/WebSession/SessionDictionary.cs b/src/WebExpress.WebCore/WebSession/Model/SessionDictionary.cs similarity index 85% rename from src/WebExpress.WebCore/WebSession/SessionDictionary.cs rename to src/WebExpress.WebCore/WebSession/Model/SessionDictionary.cs index 9a27725..22b9a67 100644 --- a/src/WebExpress.WebCore/WebSession/SessionDictionary.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionDictionary.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { /// /// Internal directory for storing session data. diff --git a/src/WebExpress.WebCore/WebSession/SessionProperty.cs b/src/WebExpress.WebCore/WebSession/Model/SessionProperty.cs similarity index 78% rename from src/WebExpress.WebCore/WebSession/SessionProperty.cs rename to src/WebExpress.WebCore/WebSession/Model/SessionProperty.cs index fc29716..59d2d63 100644 --- a/src/WebExpress.WebCore/WebSession/SessionProperty.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionProperty.cs @@ -1,4 +1,4 @@ -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { /// /// Base class of a property that can be assigned to a session. diff --git a/src/WebExpress.WebCore/WebSession/SessionPropertyAuthentification.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs similarity index 87% rename from src/WebExpress.WebCore/WebSession/SessionPropertyAuthentification.cs rename to src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs index 204ff83..32da55e 100644 --- a/src/WebExpress.WebCore/WebSession/SessionPropertyAuthentification.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs @@ -1,4 +1,4 @@ -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { public class SessionPropertyAuthentification : SessionProperty { diff --git a/src/WebExpress.WebCore/WebSession/SessionPropertyAuthorization.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs similarity index 62% rename from src/WebExpress.WebCore/WebSession/SessionPropertyAuthorization.cs rename to src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs index 7d604b0..ece2312 100644 --- a/src/WebExpress.WebCore/WebSession/SessionPropertyAuthorization.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs @@ -1,4 +1,4 @@ -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { public class SessionPropertyAuthorization : SessionProperty { diff --git a/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs similarity index 94% rename from src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs rename to src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs index 7f2f965..53e7c0b 100644 --- a/src/WebExpress.WebCore/WebSession/SessionPropertyParameter.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebMessage; -namespace WebExpress.WebCore.WebSession +namespace WebExpress.WebCore.WebSession.Model { public class SessionPropertyParameter : SessionProperty { diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 0788357..3513cb3 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -5,6 +5,7 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSession.Model; namespace WebExpress.WebCore.WebSession { diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs similarity index 98% rename from src/WebExpress.WebCore/WebSitemap/SitemapNode.cs rename to src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs index 2c2c134..b519a4e 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs @@ -3,7 +3,7 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebSitemap +namespace WebExpress.WebCore.WebSitemap.Model { /// /// A Sitemap node. diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index 7e93a78..22e6eb4 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebSitemap.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index a8273bb..5b03b50 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSitemap.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs similarity index 57% rename from src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs rename to src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs index 9532a9a..ac04777 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs @@ -1,13 +1,13 @@ using System.Collections.Generic; using WebExpress.WebCore.WebPlugin; -namespace WebExpress.WebCore.WebStatusPage +namespace WebExpress.WebCore.WebStatusPage.Model { /// /// key = plugin context /// value = ResponseDictionaryItem /// - public class StatusPageDictionary : Dictionary + internal class StatusPageDictionary : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs similarity index 53% rename from src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs rename to src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs index d17c692..555ca01 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageDictionaryItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs @@ -1,12 +1,12 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.WebStatusPage +namespace WebExpress.WebCore.WebStatusPage.Model { /// /// key = statuscode /// value = status page item /// - public class StatusPageDictionaryItem : Dictionary + internal class StatusPageDictionaryItem : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs similarity index 93% rename from src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs rename to src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs index 0a24af1..5ec05d1 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs @@ -2,9 +2,9 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebStatusPage +namespace WebExpress.WebCore.WebStatusPage.Model { - public class StatusPageItem + internal class StatusPageItem { /// /// Returns the associated plugin context. diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 9203a36..14bb243 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -6,6 +6,7 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebStatusPage.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage diff --git a/src/WebExpress.WebCore/WebTask/TaskDictionary.cs b/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs similarity index 85% rename from src/WebExpress.WebCore/WebTask/TaskDictionary.cs rename to src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs index 6a0fa33..7a78172 100644 --- a/src/WebExpress.WebCore/WebTask/TaskDictionary.cs +++ b/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace WebExpress.WebCore.WebTask +namespace WebExpress.WebCore.WebTask.Model { /// /// Directory with the current tasks. diff --git a/src/WebExpress.WebCore/WebTask/Task.cs b/src/WebExpress.WebCore/WebTask/Task.cs index 0492a71..96b5aac 100644 --- a/src/WebExpress.WebCore/WebTask/Task.cs +++ b/src/WebExpress.WebCore/WebTask/Task.cs @@ -4,12 +4,13 @@ namespace WebExpress.WebCore.WebTask { + + /// + /// Represents a task that can be executed asynchronously. + /// public class Task : ITask { - /// - /// Internal management of progress. - /// - private int _Progress { get; set; } + private int _progress; /// /// Event is triggered when the task is executed. @@ -22,7 +23,7 @@ public class Task : ITask public event EventHandler Finish; /// - /// The id of the task. + /// Returns the id of the task. /// public string Id { get; internal set; } @@ -46,8 +47,8 @@ public class Task : ITask /// public int Progress { - get => _Progress; - set => _Progress = Math.Min(value, 100); + get => _progress; + set => _progress = Math.Min(value, 100); } /// @@ -87,11 +88,11 @@ public void Run() { State = TaskState.Run; - this.Progress = 0; + Progress = 0; OnProcess(); - this.Progress = 100; + Progress = 100; State = TaskState.Finish; diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 3c56a66..2fe0945 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -3,6 +3,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebTask.Model; namespace WebExpress.WebCore.WebTask { From cabe6a5ee7da652a501a1b7bf6464287de162df2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 11 Oct 2024 22:37:23 +0200 Subject: [PATCH 025/162] reimplement StatusPageManager and add tests - reimplemented StatusPageManager to improve performance and maintainability. - added unit tests for StatusPageManager to ensure reliability and correctness. --- .../Fixture/UnitTestControlFixture.cs | 3 +- .../Manager/UnitTestApplication.cs | 34 +-- .../Manager/UnitTestModule.cs | 56 ++-- .../Manager/UnitTestStatusPageManager.cs | 184 ++++++++++++ src/WebExpress.WebCore.Test/TestPageA1X.cs | 2 +- src/WebExpress.WebCore.Test/TestPageA1Y.cs | 2 +- src/WebExpress.WebCore.Test/TestPageA1Z.cs | 4 +- .../TestResourceA1X.cs | 2 +- .../TestResourceA1Y.cs | 2 +- .../TestResourceA2X.cs | 2 +- .../TestResourceAB1X.cs | 2 +- src/WebExpress.WebCore.Test/TestRestApiA1X.cs | 2 +- src/WebExpress.WebCore.Test/TestRestApiA1Y.cs | 2 +- src/WebExpress.WebCore.Test/TestRestApiA1Z.cs | 2 +- .../TestStatusPageA400.cs | 67 +++++ .../TestStatusPageA404.cs | 50 ++++ .../TestStatusPageB404.cs | 50 ++++ .../TestStatusPageB500.cs | 50 ++++ src/WebExpress.WebCore/HttpServer.cs | 55 +--- .../InternationalizationExtensions.cs | 2 +- .../Internationalization/de | 8 +- .../Internationalization/en | 8 +- .../WebApplication/ApplicationManager.cs | 10 +- .../WebApplication/IApplicationManager.cs | 8 +- .../WebAttribute/ApplicationAttribute.cs | 4 +- .../WebAttribute/IPageAttribute.cs | 9 + .../WebAttribute/IRestApiAttribute.cs | 9 + .../WebAttribute/IconAttribute.cs | 2 +- .../WebAttribute/ScopeAttribute.cs | 2 +- .../WebAttribute/StatusCodeAttribute.cs | 24 +- .../WebAttribute/StatusResponseAttribute.cs | 20 ++ .../WebAttribute/TitleAttribute.cs | 2 +- .../WebAttribute/VersionAttribute.cs | 2 +- .../WebComponent/ComponentActivator.cs | 7 +- .../WebComponent/ComponentHub.cs | 81 +++--- .../WebComponent/IComponentHub.cs | 12 +- .../WebComponent/Model/ComponentDictionary.cs | 2 +- src/WebExpress.WebCore/WebMessage/Response.cs | 21 +- .../WebMessage/ResponseBadRequest.cs | 8 +- .../WebMessage/ResponseForbidden.cs | 8 +- .../WebMessage/ResponseHeaderFields.cs | 4 +- .../WebMessage/ResponseInternalServerError.cs | 8 +- .../WebMessage/ResponseNotFound.cs | 8 +- .../WebMessage/ResponseOK.cs | 8 +- .../ResponseRedirectPermanentlyMoved.cs | 8 +- .../ResponseRedirectTemporarilyMoved.cs | 8 +- .../WebMessage/ResponseUnauthorized.cs | 8 +- .../WebModule/ModuleManager.cs | 4 +- .../WebPage/IRenderContext.cs | 24 +- src/WebExpress.WebCore/WebPage/IVisualTree.cs | 13 + .../WebPage/IVisualTreeContext.cs | 10 - src/WebExpress.WebCore/WebPage/Page.cs | 3 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 34 ++- .../WebPage/RenderContext.cs | 26 +- src/WebExpress.WebCore/WebPage/VisualTree.cs | 12 +- .../WebPage/VisualTreeContext.cs | 18 +- .../WebRestAPI/RestApiManager.cs | 18 +- .../WebStatusPage/IStatusPage.cs | 49 +--- .../WebStatusPage/IStatusPageContext.cs | 25 +- .../WebStatusPage/IStatusPageManager.cs | 47 +++ .../Model/StatusPageDictionary.cs | 129 ++++++++- .../Model/StatusPageDictionaryItem.cs | 12 - .../WebStatusPage/Model/StatusPageItem.cs | 37 ++- .../WebStatusPage/StatusMessage.cs | 23 ++ .../WebStatusPage/StatusPageContext.cs | 28 +- .../WebStatusPage/StatusPageManager.cs | 273 ++++++++++-------- 66 files changed, 1147 insertions(+), 510 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestStatusPageA400.cs create mode 100644 src/WebExpress.WebCore.Test/TestStatusPageA404.cs create mode 100644 src/WebExpress.WebCore.Test/TestStatusPageB404.cs create mode 100644 src/WebExpress.WebCore.Test/TestStatusPageB500.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IPageAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IRestApiAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs create mode 100644 src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs delete mode 100644 src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs create mode 100644 src/WebExpress.WebCore/WebStatusPage/StatusMessage.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index ba5625a..f12a2e7 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -178,9 +178,8 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") public static RenderContext CrerateContext() { var request = CrerateRequest(); - var page = new TestPageA1X(CreratePageContext()); - return new RenderContext(page, CreratePageContext(), request); + return new RenderContext(CreratePageContext()?.ModuleContext?.ApplicationContext, request, []); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index a519425..5545172 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -25,9 +25,9 @@ public void Register() pluginManager.Register(); Assert.Equal(3, componentHub.ApplicationManager.Applications.Count()); - Assert.Equal("webexpress.webcore.test.testapplicationa", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationA))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationb", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationB))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationc", componentHub.ApplicationManager.GetApplcation(typeof(TestApplicationC))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationa", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationA))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationb", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationB))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationc", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationC))?.ApplicationId); } /// @@ -58,10 +58,10 @@ public void Id(Type applicationType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(id, applcation.ApplicationId); + Assert.Equal(id, application.ApplicationId); } /// @@ -75,10 +75,10 @@ public void Name(Type applicationType, string name) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(name, applcation.ApplicationName); + Assert.Equal(name, application.ApplicationName); } /// @@ -92,10 +92,10 @@ public void Description(Type applicationType, string description) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(description, applcation.Description); + Assert.Equal(description, application.Description); } /// @@ -109,10 +109,10 @@ public void Icon(Type applicationType, string icon) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(icon, applcation.Icon); + Assert.Equal(icon, application.Icon); } /// @@ -126,10 +126,10 @@ public void ContextPath(Type applicationType, string contextPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(contextPath, applcation.ContextPath); + Assert.Equal(contextPath, application.ContextPath); } /// @@ -143,10 +143,10 @@ public void AssetPath(Type applicationType, string assetPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(assetPath, applcation.AssetPath); + Assert.Equal(assetPath, application.AssetPath); } /// @@ -160,10 +160,10 @@ public void DataPath(Type applicationType, string dataPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var applcation = componentHub.ApplicationManager.GetApplcation(applicationType); + var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution - Assert.Equal(dataPath, applcation.DataPath); + Assert.Equal(dataPath, application.DataPath); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs index afa9239..438e428 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -18,13 +18,13 @@ public class UnitTestModule public void Register() { // preconditions - var componentManager = UnitTestControlFixture.CreateComponentHub(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = UnitTestControlFixture.CreateComponentHub(); + var pluginManager = componentHub.PluginManager as PluginManager; // test execution pluginManager.Register(); - Assert.Equal(7, componentManager.ModuleManager.Modules.Count()); + Assert.Equal(7, componentHub.ModuleManager.Modules.Count()); } /// @@ -34,14 +34,14 @@ public void Register() public void Remove() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var moduleManager = componentManager.ModuleManager as ModuleManager; - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var moduleManager = componentHub.ModuleManager as ModuleManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution moduleManager.Remove(plugin); - Assert.Empty(componentManager.ModuleManager.Modules); + Assert.Empty(componentHub.ModuleManager.Modules); } /// @@ -54,11 +54,11 @@ public void Remove() public void Id(Type applicationType, Type moduleType, string id) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); Assert.Equal(id, module.ModuleId); } @@ -73,11 +73,11 @@ public void Id(Type applicationType, Type moduleType, string id) public void Name(Type applicationType, Type moduleType, string name) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); Assert.Equal(name, module.ModuleName); } @@ -92,11 +92,11 @@ public void Name(Type applicationType, Type moduleType, string name) public void Description(Type applicationType, Type moduleType, string description) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var plugin = componentManager.PluginManager.GetPlugin(typeof(TestPlugin)); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); Assert.Equal(description, module.Description); } @@ -113,8 +113,8 @@ public void Description(Type applicationType, Type moduleType, string descriptio public void Icon(Type applicationType, Type moduleType, string icon) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution Assert.Equal(icon, module.Icon); @@ -134,8 +134,8 @@ public void Icon(Type applicationType, Type moduleType, string icon) public void ContextPath(Type applicationType, Type moduleType, string contextPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution Assert.Equal(contextPath, module.ContextPath); @@ -155,8 +155,8 @@ public void ContextPath(Type applicationType, Type moduleType, string contextPat public void AssetPath(Type applicationType, Type moduleType, string assetPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution Assert.Equal(assetPath, module.AssetPath); @@ -176,8 +176,8 @@ public void AssetPath(Type applicationType, Type moduleType, string assetPath) public void DataPath(Type applicationType, Type moduleType, string dataPath) { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var module = componentManager.ModuleManager.GetModule(applicationType, moduleType); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution Assert.Equal(dataPath, module.DataPath); @@ -190,10 +190,10 @@ public void DataPath(Type applicationType, Type moduleType, string dataPath) public void IsIComponentManager() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - Assert.True(typeof(IComponentManager).IsAssignableFrom(componentManager.ModuleManager.GetType())); + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ModuleManager.GetType())); } /// @@ -203,10 +203,10 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); // test execution - foreach (var module in componentManager.ModuleManager.Modules) + foreach (var module in componentHub.ModuleManager.Modules) { Assert.True(typeof(IContext).IsAssignableFrom(module.GetType()), $"Module context {module.GetType().Name} does not implement IContext."); } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs new file mode 100644 index 0000000..bb406ae --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -0,0 +1,184 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the status page manager. + /// + [Collection("NonParallelTests")] + public class UnitTestStatusPageManager + { + /// + /// Test the register function of the status page manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.Equal(3, componentHub.StatusPageManager.StatusPages.Count()); + } + + /// + /// Test the remove function of the status page manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var statusPageManager = componentHub.StatusPageManager as StatusPageManager; + + // test execution + statusPageManager.Remove(plugin); + + Assert.Empty(componentHub.StatusPageManager.StatusPages); + } + + /// + /// Test the id property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "webexpress.webcore.test.teststatuspagea400")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), "webexpress.webcore.test.teststatuspagea404")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), "webexpress.webcore.test.teststatuspageb404")] + public void Id(Type applicationType, Type statusPageType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); + + // test execution + Assert.Equal(id, statusPage.StatusId); + } + + /// + /// Test the title property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "webindex:homepage.label")] + + public void Title(Type applicationType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusPage = componentHub.StatusPageManager.GetStatusPage(application, resourceType); + + // test execution + Assert.Equal(id, statusPage.StatusTitle); + } + + /// + /// Test the id property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), 400)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), 404)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), 404)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageB500), null)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB500), null)] + public void Code(Type applicationType, Type statusPageType, int? code) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); + + // test execution + Assert.Equal(code, statusPage?.StatusCode); + } + + /// + /// Test the icon property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "/aca/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), "/aca/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), null)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB500), null)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPageB500), null)] + public void Icon(Type applicationType, Type statusPageType, string icon) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); + + // test execution + Assert.Equal(icon, statusPage?.StatusIcon); + } + + /// + /// Test the icon property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), 400, 400)] + [InlineData(typeof(TestApplicationA), 404, 404)] + [InlineData(typeof(TestApplicationB), 404, 404)] + [InlineData(typeof(TestApplicationB), 500, 500)] + [InlineData(typeof(TestApplicationA), 500, 500)] + public void CreateAndCheckCode(Type applicationType, int statusCode, int? expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestControlFixture.CreateHttpContext().Request); + + // test execution + Assert.Equal(expected, statusResponse?.Status); + } + + /// + /// Test the icon property of the status page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), 400, "content", "content")] + [InlineData(typeof(TestApplicationA), 500, "content", "content")] + public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestControlFixture.CreateHttpContext().Request); + + // test execution + Assert.Contains(expected, statusResponse?.Content?.ToString()); + } + + /// + /// Tests whether the status page manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.StatusPageManager.GetType())); + } + + /// + /// Tests whether the status page context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + + // test execution + foreach (var application in componentHub.StatusPageManager.StatusPages) + { + Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Page context {application.GetType().Name} does not implement IContext."); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPageA1X.cs b/src/WebExpress.WebCore.Test/TestPageA1X.cs index db80211..3ae67a6 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1X.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1X.cs @@ -23,7 +23,7 @@ public sealed class TestPageA1X : IPage public IPageContext PageContext { get; private set; } /// - /// Instillation of the page. Here, for example, managed resources can be loaded. + /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. public TestPageA1X(IPageContext pageContext) diff --git a/src/WebExpress.WebCore.Test/TestPageA1Y.cs b/src/WebExpress.WebCore.Test/TestPageA1Y.cs index d46c94e..2ac576c 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1Y.cs @@ -23,7 +23,7 @@ public sealed class TestPageA1Y : IPage public IPageContext PageContext { get; private set; } /// - /// Instillation of the page. Here, for example, managed resources can be loaded. + /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. public TestPageA1Y(IPageContext pageContext) diff --git a/src/WebExpress.WebCore.Test/TestPageA1Z.cs b/src/WebExpress.WebCore.Test/TestPageA1Z.cs index 3ada69e..e54c6fb 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1Z.cs @@ -13,13 +13,11 @@ namespace WebExpress.WebCore.Test public sealed class TestPageA1Z : Page { /// - /// Instillation of the page. Here, for example, managed resources can be loaded. + /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. private TestPageA1Z(IPageContext pageContext) - : base(pageContext) { - // test the injection if (pageContext == null) { diff --git a/src/WebExpress.WebCore.Test/TestResourceA1X.cs b/src/WebExpress.WebCore.Test/TestResourceA1X.cs index 7526fd8..79786bc 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1X.cs @@ -14,7 +14,7 @@ namespace WebExpress.WebCore.Test public sealed class TestResourceA1X : IResource { /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The context of the resource. public TestResourceA1X(IResourceContext resourceContext) diff --git a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs index 73d3a59..670f71a 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA1Y.cs @@ -13,7 +13,7 @@ namespace WebExpress.WebCore.Test public sealed class TestResourceA1Y : IResource { /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// Initialization of the resource. Here, for example, managed resources can be loaded. /// public TestResourceA1Y() { diff --git a/src/WebExpress.WebCore.Test/TestResourceA2X.cs b/src/WebExpress.WebCore.Test/TestResourceA2X.cs index 8dd75a5..fa7ba51 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA2X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA2X.cs @@ -14,7 +14,7 @@ namespace WebExpress.WebCore.Test public sealed class TestResourceA2X : IResource { /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The resource manager. /// The context of the resource. diff --git a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs index 87c166f..2d102f5 100644 --- a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceAB1X.cs @@ -13,7 +13,7 @@ namespace WebExpress.WebCore.Test public sealed class TestResourceAB1X : IResource { /// - /// Instillation of the resource. Here, for example, managed resources can be loaded. + /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The context of the resource. /// The resource manager. diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs index b59abfc..86386f5 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1X.cs @@ -15,7 +15,7 @@ namespace WebExpress.WebCore.Test public sealed class TestRestApiA1X : IRestApi { /// - /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The context of the restapi resource. public TestRestApiA1X(IRestApiContext restApiContext) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs index a6f1086..b0cc094 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs @@ -15,7 +15,7 @@ namespace WebExpress.WebCore.Test public sealed class TestRestApiA1Y : IRestApi { /// - /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The context of the restapi resource. public TestRestApiA1Y(IRestApiContext restApiContext) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs index 974279d..270c367 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs @@ -16,7 +16,7 @@ namespace WebExpress.WebCore.Test public sealed class TestRestApiA1Z : RestApi { /// - /// Instillation of the rest api resource. Here, for example, managed resources can be loaded. + /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The component hub. /// The context of the restapi resource. diff --git a/src/WebExpress.WebCore.Test/TestStatusPageA400.cs b/src/WebExpress.WebCore.Test/TestStatusPageA400.cs new file mode 100644 index 0000000..d88b1ee --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestStatusPageA400.cs @@ -0,0 +1,67 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Application()] + [StatusResponse()] + [Icon("/webexpress/icon.png")] + public sealed class TestStatusPageA400 : IStatusPage + { + /// + /// Returns or sets the status message. + /// + public string StatusMessage { get; private set; } + + /// + /// Initialization of the status page. Here, for example, managed resources can be loaded. + /// + /// The context of the status page. + /// The status message. + private TestStatusPageA400(IStatusPageContext statusPageContext, StatusMessage message) + { + // test the injection + if (statusPageContext == null) + { + throw new ArgumentNullException(nameof(statusPageContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Parameter cannot be null or empty."); + } + + StatusMessage = message?.Message; + } + + /// + /// Processing of the status page. + /// + /// The context for rendering the status page. + public void Process(IRenderContext context) + { + // test the parameter + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + + context.VisualTree.Content = new HtmlText(StatusMessage); + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestStatusPageA404.cs b/src/WebExpress.WebCore.Test/TestStatusPageA404.cs new file mode 100644 index 0000000..38adb89 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestStatusPageA404.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Application()] + [StatusResponse()] + [Icon("/webexpress/icon.png")] + public sealed class TestStatusPageA404 : IStatusPage + { + /// + /// Initialization of the status page. Here, for example, managed resources can be loaded. + /// + /// The context of the status page. + private TestStatusPageA404(IStatusPageContext statusPageContext) + { + // test the injection + if (statusPageContext == null) + { + throw new ArgumentNullException(nameof(statusPageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the status page. + /// + /// The context for rendering the status page. + public void Process(IRenderContext context) + { + // test the parameter + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestStatusPageB404.cs b/src/WebExpress.WebCore.Test/TestStatusPageB404.cs new file mode 100644 index 0000000..c640419 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestStatusPageB404.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + [Application()] + [StatusResponse()] + // [Icon("/webexpress/icon.png")] test empty icon + public sealed class TestStatusPageB404 : IStatusPage + { + /// + /// Initialization of the status page. Here, for example, managed resources can be loaded. + /// + /// The context of the status page. + private TestStatusPageB404(IStatusPageContext statusPageContext) + { + // test the injection + if (statusPageContext == null) + { + throw new ArgumentNullException(nameof(statusPageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the status page. + /// + /// The context for rendering the status page. + public void Process(IRenderContext context) + { + // test the parameter + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestStatusPageB500.cs b/src/WebExpress.WebCore.Test/TestStatusPageB500.cs new file mode 100644 index 0000000..24857d9 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestStatusPageB500.cs @@ -0,0 +1,50 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:homepage.label")] + //[Application()] test for empty application + [StatusResponse()] + [Icon("/webexpress/icon.png")] + public sealed class TestStatusPageB500 : IStatusPage + { + /// + /// Initialization of the status page. Here, for example, managed resources can be loaded. + /// + /// The context of the status page. + private TestStatusPageB500(IStatusPageContext statusPageContext) + { + // test the injection + if (statusPageContext == null) + { + throw new ArgumentNullException(nameof(statusPageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the status page. + /// + /// The context for rendering the status page. + public void Process(IRenderContext context) + { + // test the parameter + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index b34407a..07cc63f 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -21,7 +21,6 @@ using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebUri; @@ -446,11 +445,11 @@ private async Task SendResponseAsync(HttpContext context, Response response) /// /// Creates a status page /// - /// The error message. + /// The error message. /// The request. /// The plugin by searching the status page or null. /// The response. - private Response CreateStatusPage(string massage, Request request, SearchResult searchResult = null) where T : Response, new() + private Response CreateStatusPage(string message, Request request, SearchResult searchResult = null) where T : Response, new() { var response = new T() as Response; var culture = Culture; @@ -465,54 +464,18 @@ private async Task SendResponseAsync(HttpContext context, Response response) if (searchResult != null) { - var statusPage = WebEx.ComponentHub.StatusPageManager.CreateStatusPage + return WebEx.ComponentHub.StatusPageManager.CreateStatusResponse ( - massage, + message, response.Status, - searchResult?.EndpointContext?.ModuleContext?.PluginContext ?? - searchResult?.EndpointContext?.ModuleContext?.ApplicationContext?.PluginContext + searchResult?.EndpointContext?.ModuleContext?.ApplicationContext, + request ); - - if (statusPage == null) - { - return response; - } - - if (statusPage is II18N i18n) - { - i18n.Culture = culture; - } - - if (statusPage is Resource resource) - { - //resource.ApplicationContext = searchResult?.ApplicationContext ?? new ApplicationContext() - //{ - // PluginContext = searchResult?.ModuleContext?.PluginContext ?? - // searchResult?.ApplicationContext?.PluginContext, - // ApplicationId = "webex", - // ApplicationName = "WebExpress", - // ContextPath = new UriResource() - //}; - - //resource.ModuleContext = searchResult?.ModuleContext ?? new ModuleContext() - //{ - // ApplicationContext = resource.ApplicationContext, - // PluginContext = searchResult?.ModuleContext?.PluginContext ?? - // searchResult?.ApplicationContext?.PluginContext, - // ModuleId = "webex", - // ModuleName = "WebExpress", - // ContextPath = new UriResource() - //}; - - resource.Initialization(new ResourceContext(resource.ModuleContext, WebEx.ComponentHub?.ResourceManager)); - } - - return statusPage.Process(request); } - var message = $"{response.Status}" + - $"

{massage}

" + - $""; + message = $"{response.Status}" + + $"

{message}

" + + $""; response.Content = message; response.Header.ContentLength = message.Length; diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 2cd6a87..42b83fe 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -60,7 +60,7 @@ public static string I18N(this RenderContext obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, string key) { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.PageContext?.PluginContext?.PluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.ApplicationContext?.PluginContext?.PluginId, key); } ///

diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 7b8c5cc..2587ef8 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -115,9 +115,11 @@ restapimanager.resource=Seite: '{0}' für das Modul '{1}' restapimanager.methodnotsupported=Die Methode '{0}' wird nicht unterstützt. statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. -statuspagemanager.register=Status {0} wurde registriert und der Statusseite '{1}' zugewiesen. -statuspagemanager.duplicat=Der Status {0} wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. -statuspagemanager.statuscode=Ein Statuscode wurde der Ressource '{1}' für das Modul '{0}' nicht zugewiesen. +statuspagemanager.applicationless=Die Statusseite '{0}' besitzt keine Angaben zur Anwendung. +statuspagemanager.applicationrich=Die Statusseite '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. +statuspagemanager.register=Der Status '{0}' wurde registriert und der Statusseite '{1}' zugewiesen. +statuspagemanager.duplicat=Der Status '{0}' wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. +statuspagemanager.statuscode=Ein Statuscode wurde der Ressource '{1}' für die Anwendung '{0}' nicht zugewiesen. statuspagemanager.statuspage=Statuscode: '{0}' sitemapmanager.initialization=Der Sitemap-Manager wurde initialisiert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 3f3a72d..5d25a93 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -115,9 +115,11 @@ restapimanager.resource=REST API: '{0}' for module '{1}' restapimanager.methodnotsupported=The method '{0}' is not supported. statuspagemanager.initialization=The status page manager has been initialized. -statuspagemanager.register=Status {0} has been registered and assigned to the status page '{1}'. -statuspagemanager.duplicat=The status {0} has already been registered. Therefore, the status page '{1}' is not used. -statuspagemanager.statuscode=A status code has not been assigned to the resource '{1}' for the module '{0}'. +statuspagemanager.applicationless=The status page '{0}' does not have any information about the application. +statuspagemanager.applicationrich=The status page '{0}' has too many applications. The first application is used. +statuspagemanager.register=The status '{0}' has been registered and assigned to the status page '{1}'. +statuspagemanager.duplicat=The status '{0}' has already been registered. Therefore, the status page '{1}' is not used. +statuspagemanager.statuscode=A status code has not been assigned to the resource '{1}' for the application '{0}'. statuspagemanager.resource=Status code: '{0}' sitemapmanager.initialization=The sitemap manager has been initialized. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 205140a..451227c 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -211,7 +211,7 @@ public void Register(IEnumerable pluginContexts) /// /// The application id. /// The context of the application or null. - public IApplicationContext GetApplcation(string applicationId) + public IApplicationContext GetApplication(string applicationId) { if (string.IsNullOrWhiteSpace(applicationId)) return null; @@ -233,7 +233,7 @@ public IApplicationContext GetApplcation(string applicationId) ///
/// The application type. /// The context of the application or null. - public IApplicationContext GetApplcation(Type application) + public IApplicationContext GetApplication(Type application) { if (application == null) return null; @@ -254,7 +254,7 @@ public IApplicationContext GetApplcation(Type application) ///
/// The applications ids. Can contain regular expressions or * for all. /// The contexts of the applications as an enumeration. - public IEnumerable GetApplcations(IEnumerable applicationIds) + public IEnumerable GetApplications(IEnumerable applicationIds) { var list = new List(); @@ -286,7 +286,7 @@ public IEnumerable GetApplcations(IEnumerable appli ///
/// The context of the plugin. /// The contexts of the applications as an enumeration. - public IEnumerable GetApplcations(IPluginContext pluginContext) + public IEnumerable GetApplications(IPluginContext pluginContext) { if (!_dictionary.ContainsKey(pluginContext)) { @@ -414,7 +414,7 @@ private void OnRemoveApplication(IApplicationContext applicationContext) /// The shaft deep. public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { - foreach (var applicationContext in GetApplcations(pluginContext)) + foreach (var applicationContext in GetApplications(pluginContext)) { output.Add ( diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs index 543f9f1..a035234 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -30,27 +30,27 @@ public interface IApplicationManager : IComponentManager ///
/// The application id. /// The context of the application or null. - IApplicationContext GetApplcation(string applicationId); + IApplicationContext GetApplication(string applicationId); /// /// Determines the application contexts for a given application id. /// /// The application type. /// The context of the application or null. - IApplicationContext GetApplcation(Type application); + IApplicationContext GetApplication(Type application); /// /// Determines the application contexts for the given application ids. /// /// The applications ids. Can contain regular expressions or * for all. /// The contexts of the applications as an enumeration. - IEnumerable GetApplcations(IEnumerable applicationIds); + IEnumerable GetApplications(IEnumerable applicationIds); /// /// Determines the application contexts for the given plugin. /// /// The context of the plugin. /// The contexts of the applications as an enumeration. - IEnumerable GetApplcations(IPluginContext pluginContext); + IEnumerable GetApplications(IPluginContext pluginContext); } } diff --git a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs index 36e471b..6a3e658 100644 --- a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Application assignment attribute of the application ID. ///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute + public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public ApplicationAttribute(string applicationId) /// /// The type of the application. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute where T : class, IApplication + public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute where T : class, IApplication { } diff --git a/src/WebExpress.WebCore/WebAttribute/IPageAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IPageAttribute.cs new file mode 100644 index 0000000..57ef491 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IPageAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a page assignment attribute. + /// + public interface IPageAttribute : IEndpointAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IRestApiAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IRestApiAttribute.cs new file mode 100644 index 0000000..976ecab --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IRestApiAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a rest api assignment attribute. + /// + public interface IRestApiAttribute : IEndpointAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 8b2a374..76ea0b6 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute + public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs index 9aff2fd..3d42cfa 100644 --- a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// The range in which the component is valid. /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class ScopeAttribute : Attribute, IEndpointAttribute where T : class, IScope + public class ScopeAttribute : Attribute, IPageAttribute where T : class, IScope { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs index 25e7376..44e838b 100644 --- a/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/StatusCodeAttribute.cs @@ -1,17 +1,23 @@ -using System; - -namespace WebExpress.WebCore.WebAttribute +namespace WebExpress.WebCore.WebAttribute { - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class StatusCodeAttribute : System.Attribute, IApplicationAttribute + /// + /// Specifies the status code for an HTTP response (see RFC 7231). + /// + [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)] + public class StatusCodeAttribute : System.Attribute { /// - /// Initializes a new instance of the class. + /// Gets the status code. /// - /// The status code. - public StatusCodeAttribute(int status) - { + public int StatusCode { get; } + /// + /// Initializes a new instance of the class with the specified status code. + /// + /// The status code. + public StatusCodeAttribute(int code) + { + StatusCode = code; } } } diff --git a/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs b/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs new file mode 100644 index 0000000..298278c --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs @@ -0,0 +1,20 @@ +using System; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebAttribute +{ + /// Specifies the status code for a starus page. + /// + /// The type of the response. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class StatusResponseAttribute : Attribute, IStatusPageAttribute where T : Response, new() + { + /// + /// Initializes a new instance of the class. + /// + public StatusResponseAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs index 49fbcf4..c7d4826 100644 --- a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class TitleAttribute : System.Attribute, IEndpointAttribute + public class TitleAttribute : System.Attribute, IPageAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs index dc41195..3cfd8dc 100644 --- a/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Specifying a version for a rest api. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class VersionAttribute : Attribute, IEndpointAttribute + public class VersionAttribute : Attribute, IRestApiAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index e27f999..83cd8b5 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -55,8 +55,9 @@ public static T CreateInstance(Type componentType, ComponentHub componentMana /// The type of the component to create. /// The context to pass to the component's constructor. /// The component manager to use for dependency injection. + /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, C context, IComponentHub componentManager) where T : class, IComponent where C : IContext + public static T CreateInstance(Type componentType, C context, IComponentHub componentManager, params object[] advancedParameters) where T : class, IComponent where C : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -75,7 +76,9 @@ public static T CreateInstance(Type componentType, C context, IComponentHu parameter.ParameterType == typeof(C) ? context : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? - .GetValue(componentManager) ?? null + .GetValue(componentManager) ?? + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null ).ToArray(); if (constructor.Invoke(parameterValues) is T component) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 4e34f84..be67f2a 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -25,6 +25,7 @@ namespace WebExpress.WebCore.WebComponent /// public class ComponentHub : IComponentHub { + private readonly ComponentDictionary _dictionary = []; private readonly InternationalizationManager _internationalizationManager; private readonly PluginManager _pluginManager; private readonly ApplicationManager _applicationManager; @@ -33,6 +34,7 @@ public class ComponentHub : IComponentHub private readonly PageManager _pageManager; private readonly RestApiManager _restApiManager; private readonly SitemapManager _sitemapManager; + private readonly StatusPageManager _statusPageManager; /// /// An event that fires when an component is added. @@ -49,11 +51,6 @@ public class ComponentHub : IComponentHub /// public IHttpServerContext HttpServerContext { get; private set; } - /// - /// Returns the directory where the components are listed. - /// - private ComponentDictionary Dictionary { get; } = []; - /// /// Returns all registered managers. /// @@ -70,11 +67,11 @@ public class ComponentHub : IComponentHub _restApiManager, EventManager, JobManager, - StatusPageManager, + _statusPageManager, _internationalizationManager, SessionManager, TaskManager - }.Concat(Dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); + }.Concat(_dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); /// /// Returns the log manager. @@ -118,12 +115,6 @@ public class ComponentHub : IComponentHub /// The instance of the job manager. public JobManager JobManager { get; private set; } - /// - /// Returns the status page manager. - /// - /// The instance of the status page manager. - public StatusPageManager StatusPageManager { get; private set; } - /// /// Returns the resource manager. /// @@ -148,6 +139,12 @@ public class ComponentHub : IComponentHub /// The instance of the sitemap manager. public ISitemapManager SitemapManager => _sitemapManager; + /// + /// Returns the status page manager. + /// + /// The instance of the status page manager. + public IStatusPageManager StatusPageManager => _statusPageManager; + /// /// Returns the internationalization manager. /// @@ -185,7 +182,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; _pageManager = CreateInstance(typeof(PageManager)) as PageManager; _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; - StatusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; + _statusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; SessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; @@ -253,7 +250,7 @@ private IComponentManager CreateInstance(Type componentType) /// The instance of the component or null. public IComponentManager GetComponent(string id) { - return Dictionary.Values + return _dictionary.Values .SelectMany(x => x) .Where(x => x.ComponentId.Equals(id, StringComparison.OrdinalIgnoreCase)) .Select(x => x.ComponentInstance) @@ -267,7 +264,7 @@ public IComponentManager GetComponent(string id) /// The instance of the component or null. public T GetComponent() where T : IComponentManager { - return (T)Dictionary.Values + return (T)_dictionary.Values .SelectMany(x => x) .Where(x => x.ComponentClass == typeof(T)) .Select(x => x.ComponentInstance) @@ -281,15 +278,15 @@ public T GetComponent() where T : IComponentManager internal void Register(IPluginContext pluginContext) { // the plugin has already been registered - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.ContainsKey(pluginContext)) { return; } var assembly = pluginContext.Assembly; - Dictionary.Add(pluginContext, []); - var componentItems = Dictionary[pluginContext]; + _dictionary.Add(pluginContext, []); + var componentItems = _dictionary[pluginContext]; foreach (var type in assembly.GetExportedTypes().Where(x => x.IsClass && x.IsSealed && x.GetInterface(typeof(IComponentManager).Name) != null)) { @@ -300,12 +297,12 @@ internal void Register(IPluginContext pluginContext) if (!componentItems.Where(x => x.ComponentId.Equals(id, StringComparison.OrdinalIgnoreCase)).Any()) { - componentItems.Add(new ComponentItem() + componentItems = componentItems.Concat([ new ComponentItem() { ComponentClass = type, ComponentId = id, ComponentInstance = componentInstance - }); + }]); HttpServerContext.Log.Debug ( @@ -347,7 +344,7 @@ internal void BootComponent(IPluginContext pluginContext) _applicationManager.Boot(pluginContext); _moduleManager.Boot(pluginContext); - foreach (var component in Dictionary.Values + foreach (var component in _dictionary.Values .Where(x => x is IExecutableElements) .Select(x => x as IExecutableElements)) { @@ -402,7 +399,7 @@ internal void ShutDownComponent(IPluginContext pluginContext) _applicationManager.ShutDown(pluginContext); _moduleManager.ShutDown(pluginContext); - foreach (var component in Dictionary.Values + foreach (var component in _dictionary.Values .Where(x => x is IExecutableElements) .Select(x => x as IExecutableElements)) { @@ -421,33 +418,25 @@ public void Remove(IPluginContext pluginContext) return; } - if (Dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out IEnumerable componentItems)) { - return; - } - - var componentItems = Dictionary[pluginContext]; - - if (!componentItems.Any()) - { - return; - } + if (!componentItems.Any()) + { + return; + } - foreach (var componentItem in componentItems) - { - OnRemoveComponent(componentItem.ComponentInstance); + foreach (var componentItem in componentItems) + { + OnRemoveComponent(componentItem.ComponentInstance); - HttpServerContext.Log.Debug - ( - _internationalizationManager.Translate("webexpress:componentmanager.remove") - ); + HttpServerContext.Log.Debug + ( + _internationalizationManager.Translate("webexpress:componentmanager.remove") + ); + } } - _moduleManager.Remove(pluginContext); - _applicationManager.Remove(pluginContext); - _pluginManager.Remove(pluginContext); - - Dictionary.Remove(pluginContext); + _dictionary.Remove(pluginContext); } /// @@ -490,7 +479,7 @@ internal void LogStatus() _applicationManager.PrepareForLog(pluginContext, output, 4); _moduleManager.PrepareForLog(pluginContext, output, 4); _resourceManager.PrepareForLog(pluginContext, output, 4); - StatusPageManager.PrepareForLog(pluginContext, output, 4); + _statusPageManager.PrepareForLog(pluginContext, output, 4); JobManager.PrepareForLog(pluginContext, output, 4); } diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index ccbf411..212e8d5 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -84,12 +84,6 @@ public interface IComponentHub : IComponentManager /// The instance of the job manager. JobManager JobManager { get; } - /// - /// Returns the status page manager. - /// - /// The instance of the status page manager. - StatusPageManager StatusPageManager { get; } - /// /// Returns the resource manager. /// @@ -114,6 +108,12 @@ public interface IComponentHub : IComponentManager /// The instance of the sitemap manager. ISitemapManager SitemapManager { get; } + /// + /// Returns the status page manager. + /// + /// The instance of the status page manager. + IStatusPageManager StatusPageManager { get; } + /// /// Returns the internationalization manager. /// diff --git a/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs b/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs index 99ee85e..d49fc29 100644 --- a/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs +++ b/src/WebExpress.WebCore/WebComponent/Model/ComponentDictionary.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebComponent.Model /// key = plugin /// value = component item /// - internal class ComponentDictionary : Dictionary> + internal class ComponentDictionary : Dictionary> { } diff --git a/src/WebExpress.WebCore/WebMessage/Response.cs b/src/WebExpress.WebCore/WebMessage/Response.cs index 633bec2..504238d 100644 --- a/src/WebExpress.WebCore/WebMessage/Response.cs +++ b/src/WebExpress.WebCore/WebMessage/Response.cs @@ -1,32 +1,35 @@ -namespace WebExpress.WebCore.WebMessage +using System.Reflection; +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response according to RFC 2616 Section 6. /// - public class Response + public abstract class Response { /// - /// Setzt oder liefert die Optionen + /// Returns the response header fields. /// public ResponseHeaderFields Header { get; } = new ResponseHeaderFields(); /// - /// Setzt oder liefert den Content + /// Returns or sets the response content. /// public object Content { get; set; } /// - /// Liefert oder setzt den Statuscode + /// Returns the status code of the response. /// - public int Status { get; protected set; } + public int Status => GetType().GetCustomAttribute().StatusCode; /// - /// Liefert oder setzt den Statustext + /// Returns or sets the reason phrase of the response. /// public string Reason { get; protected set; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the Response class. /// protected Response() { diff --git a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs index 950a6e4..6f23048 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response for a bad request (HTTP 400). See RFC 2616 Section 6 /// + [StatusCode(400)] public class ResponseBadRequest : Response { /// @@ -11,7 +14,6 @@ public class ResponseBadRequest : Response public ResponseBadRequest() { var content = "404404 - Bad Request"; - Status = 400; Reason = "Bad Request"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs index 459a187..66debf6 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response according to RFC 2616 Section 6. /// + [StatusCode(403)] public class ResponseForbidden : Response { /// @@ -11,7 +14,6 @@ public class ResponseForbidden : Response public ResponseForbidden() { var content = "403403 - Forbidden"; - Status = 403; Reason = "Forbidden"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs index e543b16..25420f6 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 + /// see RFC 2616 /// public class ResponseHeaderFields { @@ -25,7 +25,7 @@ public class ResponseHeaderFields public string ContentLanguage { get; set; } /// - /// Liefert oder setzt die Direktiven für das Caching (siehe RFC 7234) + /// Liefert oder setzt die Direktiven für das Caching (see RFC 7234) /// public string CacheControl { get; set; } diff --git a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs index 5d2dcdc..cc5b704 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response for the HTTP 500 Internal Server Error status code according to RFC 2616 Section 6. /// + [StatusCode(500)] public class ResponseInternalServerError : Response { /// @@ -11,7 +14,6 @@ public class ResponseInternalServerError : Response public ResponseInternalServerError() { var content = "404500 - Internal Server Error"; - Status = 500; Reason = "Internal Server Error"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs index 72b8f02..9e7a7d0 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response for a resource not found (404) according to RFC 2616 Section 6. /// + [StatusCode(404)] public class ResponseNotFound : Response { /// @@ -11,7 +14,6 @@ public class ResponseNotFound : Response public ResponseNotFound() { var content = "404404 - Not Found"; - Status = 404; Reason = "Not Found"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseOK.cs b/src/WebExpress.WebCore/WebMessage/ResponseOK.cs index cf4b830..87d7a81 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseOK.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseOK.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a successful response according to RFC 2616 Section 6. /// + [StatusCode(200)] public class ResponseOK : Response { /// @@ -10,7 +13,6 @@ public class ResponseOK : Response /// public ResponseOK() { - Status = 200; Reason = "OK"; } } diff --git a/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs b/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs index b4ccd71..2c57bf7 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseRedirectPermanentlyMoved.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response according to RFC 2616 Section 6. /// + [StatusCode(301)] public class ResponseRedirectPermanentlyMoved : Response { /// @@ -10,7 +13,6 @@ public class ResponseRedirectPermanentlyMoved : Response /// public ResponseRedirectPermanentlyMoved(string location) { - Status = 301; Reason = "permanently moved"; Header.Location = location; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs b/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs index 1d00b4b..c5576ac 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseRedirectTemporarilyMoved.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response according to RFC 2616 Section 6. /// + [StatusCode(302)] public class ResponseRedirectTemporarilyMoved : Response { /// @@ -10,7 +13,6 @@ public class ResponseRedirectTemporarilyMoved : Response /// public ResponseRedirectTemporarilyMoved(string location) { - Status = 302; Reason = "temporarily moved"; //Content = ""; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs index 6c6a093..f7fe40a 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebMessage +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.WebMessage { /// - /// siehe RFC 2616 Tz. 6 + /// Represents a response indicating that the request requires user authentication. See RFC 2616 Section 6 /// + [StatusCode(401)] public class ResponseUnauthorized : Response { /// @@ -10,7 +13,6 @@ public class ResponseUnauthorized : Response /// public ResponseUnauthorized() { - Status = 401; Reason = "OK"; Header.WWWAuthenticate = true; diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs index 77a11a9..a5d520e 100644 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ b/src/WebExpress.WebCore/WebModule/ModuleManager.cs @@ -268,7 +268,7 @@ private void DetachFromApplication(IApplicationContext applicationContext) /// The context of the module or null. public IModuleContext GetModule(Type applicationType, Type moduleType) { - var applicationContext = _componentManager.ApplicationManager.GetApplcation(applicationType); + var applicationContext = _componentManager.ApplicationManager.GetApplication(applicationType); var item = _dictionary.Values .SelectMany(x => x.Values) .Where(x => x.Dictionary.ContainsKey(applicationContext)) @@ -300,7 +300,7 @@ public IModuleContext GetModule(IApplicationContext applicationContext, string m } /// - /// Determines the module for a given application context and module id. + /// Determines the module for a given application context and module type. /// /// The context of the application. /// The module class. diff --git a/src/WebExpress.WebCore/WebPage/IRenderContext.cs b/src/WebExpress.WebCore/WebPage/IRenderContext.cs index e69b07d..733c577 100644 --- a/src/WebExpress.WebCore/WebPage/IRenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/IRenderContext.cs @@ -1,18 +1,18 @@ -using System.Globalization; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage { /// - /// Represents the interface of the context in which rendering occurs, providing access to the page, request, culture, and visual tree. + /// Represents the interface of the context in which rendering occurs. /// public interface IRenderContext { /// - /// Returns the page where is rendered. + /// Returns the application context. /// - IPage Page { get; } + IApplicationContext ApplicationContext { get; } /// /// Returns the request. @@ -20,19 +20,9 @@ public interface IRenderContext Request Request { get; } /// - /// The uri of the request. + /// Returns the scopes. /// - UriResource Uri { get; } - - /// - /// Returns the culture. - /// - CultureInfo Culture { get; } - - /// - /// Provides the context of the associated resource. - /// - IPageContext PageContext { get; } + IEnumerable Scopes { get; } /// /// Returns the contents of a page. diff --git a/src/WebExpress.WebCore/WebPage/IVisualTree.cs b/src/WebExpress.WebCore/WebPage/IVisualTree.cs index e7f3181..412daae 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTree.cs @@ -3,8 +3,16 @@ namespace WebExpress.WebCore.WebPage { + /// + /// Represents a visual tree for rendering a web page. + /// public interface IVisualTree { + /// + /// Returns the title of the html document. + /// + string Title { get; set; } + /// /// Returns the favicon. /// @@ -45,6 +53,11 @@ public interface IVisualTree /// List> Meta { get; } + /// + /// Returns or sets the content. + /// + IHtmlNode Content { get; set; } + /// /// Adds a java script. /// diff --git a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs index 66b7c20..b23a118 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs @@ -9,11 +9,6 @@ namespace WebExpress.WebCore.WebPage /// public interface IVisualTreeContext { - /// - /// Returns the page where is rendered. - /// - IPage Page { get; } - /// /// Returns the request. /// @@ -28,10 +23,5 @@ public interface IVisualTreeContext /// Returns the culture. /// CultureInfo Culture { get; } - - /// - /// Provides the context of the associated page. - /// - IPageContext PageContext { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index ccc4f3d..82ee5ac 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -20,9 +20,8 @@ /// Initializes a new instance of the class. /// /// The context of the page. - public Page(IPageContext pageContext) + public Page() { - PageContext = pageContext; } /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index bf6d2f8..bce8a39 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -12,7 +12,6 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSitemap; -using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage @@ -83,17 +82,18 @@ private PageManager(IComponentHub componentManager, IHttpServerContext httpServe { var type = endpoint.GetType(); var context = default(IRenderContext); + var pageContetx = endpontContext as IPageContext; if (type.IsGenericType) { var typeOfT = type.GetGenericArguments()[0]; - object[] parameters = [endpoint as IPage, endpontContext as IPageContext, request]; + var parameters = new object[] { endpoint as IPage, endpontContext as IPageContext, request }; context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; } else { - context = new RenderContext(endpoint as IPage, endpontContext as IPageContext, request); + context = new RenderContext(endpontContext.ModuleContext?.ApplicationContext, request, pageContetx.Scopes); } (endpoint as IPage).Process(context); @@ -272,7 +272,7 @@ private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) return instance; } - return resourceItem?.Instance as IPage; + return resourceItem?.Instance; } /// @@ -293,8 +293,7 @@ public void Register(IPluginContext pluginContext) foreach (var resourceType in assembly.GetTypes() .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) - .Where(x => x.GetInterface(typeof(IPage).Name) != null) - .Where(x => x.GetInterface(typeof(IStatusPage).Name) == null)) + .Where(x => x.GetInterface(typeof(IPage).Name) != null)) { var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); @@ -317,10 +316,6 @@ public void Register(IPluginContext pluginContext) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } - else if (customAttribute.AttributeType == typeof(TitleAttribute)) - { - title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); @@ -337,10 +332,6 @@ public void Register(IPluginContext pluginContext) { moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); } - else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) - { - scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -356,6 +347,21 @@ public void Register(IPluginContext pluginContext) } } + foreach (var customAttribute in resourceType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute)))) + { + var buf = typeof(ModuleAttribute<>); + + if (customAttribute.AttributeType == typeof(TitleAttribute)) + { + title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) + { + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + } + } + if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) { scopes.Add(resourceType.FullName?.ToLower()); diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 899fb53..5c19ecd 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,4 +1,6 @@ -using System.Globalization; +using System.Collections.Generic; +using System.Globalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; @@ -10,9 +12,9 @@ namespace WebExpress.WebCore.WebPage public class RenderContext : IRenderContext { /// - /// Returns the page where is rendered. + /// Returns the application context. /// - public IPage Page { get; protected set; } + public IApplicationContext ApplicationContext { get; protected set; } /// /// Returns the request. @@ -30,9 +32,9 @@ public class RenderContext : IRenderContext public CultureInfo Culture => Request?.Culture; /// - /// Provides the context of the associated page. + /// Returns the scopes. /// - public IPageContext PageContext { get; protected set; } + public IEnumerable Scopes { get; } /// /// Returns the contents of a page. @@ -50,23 +52,23 @@ public RenderContext() /// /// Initializes a new instance of the class. /// - /// The page where the rendering is taking place. - /// The context of the associated page. + /// >The application context. /// The request associated with the rendering context. - public RenderContext(IPage page, IPageContext pageContext, Request request) + /// The scopes associated with the rendering context. + public RenderContext(IApplicationContext applicationContext, Request request, IEnumerable scopes) : this() { - Page = page; - PageContext = pageContext; + ApplicationContext = applicationContext; Request = request; + Scopes = scopes; } /// /// Copy-Constructor /// - /// The context to copy./param> + /// The context to copy. public RenderContext(RenderContext context) - : this(context?.Page, context.PageContext, context?.Request) + : this(context?.ApplicationContext, context?.Request, context?.Scopes) { } diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index 319031e..6ea27ae 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -10,6 +10,11 @@ namespace WebExpress.WebCore.WebPage /// public class VisualTree : IVisualTree { + /// + /// Returns the title of the html document. + /// + public string Title { get; set; } + /// /// Returns the favicons. /// @@ -51,9 +56,9 @@ public class VisualTree : IVisualTree public List> Meta { get; } = []; /// - /// Returns the content. + /// Returns or sets the content. /// - public IHtmlNode Content { get; } + public IHtmlNode Content { get; set; } /// /// Initializes a new instance of the class. @@ -110,9 +115,8 @@ public virtual void AddHeaderScriptLinks(string url) public virtual IHtmlNode Render(IVisualTreeContext context) { var html = new HtmlElementRootHtml(); - html.Head.Title = I18N.Translate(context.Request, context.PageContext.PageTitle); + html.Head.Title = I18N.Translate(context.Request, Title); html.Head.Favicons = Favicons?.Select(x => new Favicon(x.Url, x.Mediatype)); - //html.Head.Base = Context.ContextPath.ToString(); html.Head.Styles = Styles; html.Head.Meta = Meta; html.Head.Scripts = HeaderScripts; diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs index 1d57699..f3e2c2f 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -9,11 +9,6 @@ namespace WebExpress.WebCore.WebPage /// public class VisualTreeContext : IVisualTreeContext { - /// - /// Returns the page where is rendered. - /// - public IPage Page { get; protected set; } - /// /// Returns the request. /// @@ -29,21 +24,12 @@ public class VisualTreeContext : IVisualTreeContext /// public CultureInfo Culture => Request?.Culture; - /// - /// Provides the context of the associated page. - /// - public IPageContext PageContext { get; protected set; } - /// /// Initializes a new instance of the class. /// - /// The page where the rendering is taking place. - /// The context of the associated resource. /// The request associated with the rendering context. - public VisualTreeContext(IPage page, IPageContext resourceContext, Request request) + public VisualTreeContext(Request request) { - Page = page; - PageContext = resourceContext; Request = request; } @@ -52,7 +38,7 @@ public VisualTreeContext(IPage page, IPageContext resourceContext, Request reque /// /// The context to copy./param> public VisualTreeContext(IRenderContext context) - : this(context?.Page, context.PageContext, context?.Request) + : this(context?.Request) { } } diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index ade3fd0..688a494 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -13,7 +13,6 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebRestApi.Model; using WebExpress.WebCore.WebSitemap; -using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi @@ -314,8 +313,7 @@ public void Register(IPluginContext pluginContext) foreach (var resourceType in assembly.GetTypes() .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) - .Where(x => x.GetInterface(typeof(IRestApi).Name) != null) - .Where(x => x.GetInterface(typeof(IStatusPage).Name) == null)) + .Where(x => x.GetInterface(typeof(IRestApi).Name) != null)) { var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); @@ -365,10 +363,6 @@ public void Register(IPluginContext pluginContext) var method = (CrudMethod)customAttribute.ConstructorArguments.FirstOrDefault().Value; methods.Add(method); } - else if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) - { - version = Convert.ToUInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); - } else if (customAttribute.AttributeType == typeof(CacheAttribute)) { cache = true; @@ -379,6 +373,16 @@ public void Register(IPluginContext pluginContext) } } + foreach (var customAttribute in resourceType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IRestApiAttribute)))) + { + if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) + { + version = Convert.ToUInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); + } + + } + if (string.IsNullOrEmpty(moduleId)) { // no module specified diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs index e6ac3fd..fd4e0b2 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs @@ -1,50 +1,25 @@ -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { /// /// Interface of the status pages. /// - public interface IStatusPage + public interface IStatusPage : IComponent { /// - /// Returns the resource context where the resource exists. + /// Processing of the status page. /// - IPageContext ResourceContext { get; } - - /// - /// Returns or sets the status code. - /// - int StatusCode { get; set; } - - /// - /// Returns or sets the status title. - /// - string StatusTitle { get; set; } - - /// - /// Returns or sets the status message. - /// - string StatusMessage { get; set; } - - /// - /// Returns or sets the status icon. - /// - UriResource StatusIcon { get; set; } - - /// - /// Initialization - /// - /// The context of the resource. - void Initialization(IPageContext resourceContext); + /// The context for rendering the status page. + void Process(IRenderContext context); + } - /// - /// Processing of the resource. - /// - /// The request. - /// The response. - Response Process(Request request); + /// + /// Defines the contract for a status page resource that can be rendered using a specific context. + /// + /// The type of the render context. + public interface IStatusPage : IStatusPage where T : IRenderContext + { } } diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs index b3fbf35..c6bc891 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs @@ -1,28 +1,43 @@ -using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { - public interface IStatusPageContext + /// + /// Represents the context for a status page. + /// + public interface IStatusPageContext : IContext { /// /// Returns the associated plugin context. /// IPluginContext PluginContext { get; } + /// + /// Returns the associated application context. + /// + IApplicationContext ApplicationContext { get; } + + /// + /// Returns or sets the status id. + /// + string StatusId { get; } + /// /// Returns or sets the status code. /// - int Code { get; } + int StatusCode { get; } /// /// Returns or sets the status title. /// - string Title { get; } + string StatusTitle { get; } /// /// Returns or sets the status icon. /// - UriResource Icon { get; } + UriResource StatusIcon { get; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs new file mode 100644 index 0000000..7aac0c6 --- /dev/null +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebStatusPage +{ + /// + /// Management of status pages. + /// + public interface IStatusPageManager : IComponent + { + /// + /// An event that fires when an status page is added. + /// + event EventHandler AddStatusPage; + + /// + /// An event that fires when an status page is removed. + /// + event EventHandler RemoveStatusPage; + + /// + /// Returns all status pages. + /// + IEnumerable StatusPages { get; } + + /// + /// Determines the status page for a given application context and status type. + /// + /// The context of the application. + /// The status page class. + /// The context of the status page or null. + IStatusPageContext GetStatusPage(IApplicationContext applicationContext, Type statusPageClass); + + /// + /// Creates a status response. + /// + /// The status message. + /// The status code. + /// The application context where the status pages are located or null for an undefined page (may be from another application) that matches the status code. + /// The request. + /// The response or null. + Response CreateStatusResponse(string message, int status, IApplicationContext applicationContext, Request request); + } +} diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs index ac04777..f42a1f9 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs @@ -1,13 +1,134 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebStatusPage.Model { /// - /// key = plugin context - /// value = ResponseDictionaryItem + /// Represents a dictionary that provides a mapping from plugin contexts to dictionaries of application contexts and status page items. /// - internal class StatusPageDictionary : Dictionary + internal class StatusPageDictionary : Dictionary>> { + /// + /// Adds a status page item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The status code. + /// The status page item. + /// True if the status page item was added successfully, false if an element with the same status code already exists. + public bool AddStatusPageItem(IPluginContext pluginContext, IApplicationContext applicationContext, int statusCode, StatusPageItem statusPageItem) + { + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = []; + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = []; + } + + var statusCodeDict = appContextDict[applicationContext]; + + if (statusCodeDict.ContainsKey(statusCode)) + { + return false; // item with the same status code already exists + } + + statusCodeDict[statusCode] = statusPageItem; + + return true; + } + + /// + /// Removes a status page item from the dictionary. + /// + /// The plugin context. + /// The application context. + /// The status code. + public void RemoveStatusPageItem(IPluginContext pluginContext, IApplicationContext applicationContext, int statusCode) + { + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var statusCodeDict = appContextDict[applicationContext]; + + if (statusCodeDict.ContainsKey(statusCode)) + { + statusCodeDict.Remove(statusCode); + + if (statusCodeDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the status page item from the dictionary. + /// + /// The application context. + /// The status code. + /// The status page item if found, otherwise null. + public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, int statusCode) + { + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var statusCodeDict = appContextDict[applicationContext]; + + if (statusCodeDict.ContainsKey(statusCode)) + { + return statusCodeDict[statusCode]; + } + } + } + + return null; + } + + /// + /// Returns the status page item based on the application context and status page type. + /// + /// The application context. + /// The type of the status page. + /// The status page item if found, otherwise null. + public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, Type statusPageType) + { + return Values.SelectMany(applicationDict => applicationDict.Values) + .SelectMany(statusDict => statusDict.Values) + .FirstOrDefault(statusPageItem => statusPageItem.StatusPageClass == statusPageType); + } + + /// + /// Returns all StatusPageContexts for a given PluginContext. + /// + /// The PluginContext. + /// An IEnumerable of StatusPageContexts. + public IEnumerable GetStatusPageContexts(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .Select(item => item.StatusPageContext); + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs deleted file mode 100644 index 555ca01..0000000 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionaryItem.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebStatusPage.Model -{ - /// - /// key = statuscode - /// value = status page item - /// - internal class StatusPageDictionaryItem : Dictionary - { - } -} diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs index 5ec05d1..452a082 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs @@ -1,44 +1,49 @@ using System; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage.Model { + /// + /// Represents a status page item. + /// internal class StatusPageItem { /// - /// Returns the associated plugin context. + /// Returns the status page id. /// - public IPluginContext PluginContext { get; internal set; } + public string StatusPageId { get; internal set; } /// - /// Returns or sets the resource id. + /// Returns the associated plugin context. /// - public string Id { get; internal set; } + public IPluginContext PluginContext { get; internal set; } /// - /// Returns or sets the status code. + /// Returns the associated application context. /// - public int StatusCode { get; internal set; } + public IApplicationContext ApplicationContext { get; internal set; } /// - /// Returns or sets the status title. + /// Returns status page context. /// - public string Title { get; internal set; } + public StatusPageContext StatusPageContext { get; internal set; } /// - /// Returns or sets the status icon. + /// Returns the status code. /// - public UriResource Icon { get; internal set; } + public Type StatusResponse { get; internal set; } /// - /// Returns or sets the type of status page. + /// Returns the type of status page. /// public Type StatusPageClass { get; internal set; } - ///// - ///// Returns or sets the module id. - ///// - //public string ModuleId { get; internal set; } + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusMessage.cs b/src/WebExpress.WebCore/WebStatusPage/StatusMessage.cs new file mode 100644 index 0000000..694e6bf --- /dev/null +++ b/src/WebExpress.WebCore/WebStatusPage/StatusMessage.cs @@ -0,0 +1,23 @@ +namespace WebExpress.WebCore.WebStatusPage +{ + + /// + /// Represents a status message. + /// + public class StatusMessage + { + /// + /// Returns the message. + /// + public string Message { get; private set; } + + /// + /// Initializes a new instance of the class with the specified message. + /// + /// The message. + public StatusMessage(string message) + { + Message = message; + } + } +} diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs index 7e36d6b..78a0a6f 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs @@ -1,8 +1,12 @@ -using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { + /// + /// Represents the context for a status page. + /// public class StatusPageContext : IStatusPageContext { /// @@ -11,18 +15,28 @@ public class StatusPageContext : IStatusPageContext public IPluginContext PluginContext { get; internal set; } /// - /// Returns or sets the status code. + /// Returns the associated application context. /// - public int Code { get; internal set; } + public IApplicationContext ApplicationContext { get; internal set; } /// - /// Returns or sets the status title. + /// Returns or sets the status id. /// - public string Title { get; internal set; } + public string StatusId { get; internal set; } /// - /// Returns or sets the status icon. + /// Returns the status code. /// - public UriResource Icon { get; internal set; } + public int StatusCode { get; internal set; } + + /// + /// Returns the status title. + /// + public string StatusTitle { get; internal set; } + + /// + /// Returns the status icon. + /// + public UriResource StatusIcon { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 14bb243..82bef37 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebStatusPage.Model; @@ -14,36 +17,30 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Management of status pages. /// - public class StatusPageManager : IComponentManagerPlugin, ISystemComponent + public class StatusPageManager : IStatusPageManager, IComponentManagerPlugin, ISystemComponent { private readonly IComponentHub _componentManager; private readonly IHttpServerContext _httpServerContext; private readonly StatusPageDictionary _dictionary = []; - private readonly StatusPageDictionaryItem _defaults = []; + private readonly Dictionary _defaults = []; /// /// An event that fires when an status page is added. /// - public event EventHandler AddStatusPage; + public event EventHandler AddStatusPage; /// /// An event that fires when an status page is removed. /// - public event EventHandler RemoveStatusPage; + public event EventHandler RemoveStatusPage; /// /// Returns all status pages. /// public IEnumerable StatusPages => _dictionary.Values .SelectMany(x => x.Values) - .Select(x => new StatusPageContext() - { - PluginContext = x.PluginContext, - Code = x.StatusCode, - Title = x.Title, - Icon = x.Icon - } - ); + .SelectMany(x => x.Values) + .Select(x => x.StatusPageContext); /// /// Initializes a new instance of the class. @@ -84,19 +81,23 @@ public void Register(IPluginContext pluginContext) .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IStatusPage).Name) != null)) { - var id = resource.Name?.ToLower(); - var statusCode = -1; + var id = resource.FullName?.ToLower(); + var applicationIds = new List(); + var statusResponse = typeof(ResponseInternalServerError); var icon = string.Empty; var title = resource.Name; - //var moduleId = string.Empty; var defaultItem = false; foreach (var customAttribute in resource.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IApplicationAttribute)))) + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IStatusPageAttribute)))) { - if (customAttribute.AttributeType == typeof(StatusCodeAttribute)) + if (customAttribute.AttributeType.Name == typeof(StatusResponseAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(StatusResponseAttribute<>).Namespace) { - statusCode = Convert.ToInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); + statusResponse = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + } + else if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) + { + applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); } else if (customAttribute.AttributeType == typeof(TitleAttribute)) { @@ -106,46 +107,67 @@ public void Register(IPluginContext pluginContext) { icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } - //else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) - //{ - // moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - //} - } - - foreach (var customAttribute in resource.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IStatusPageAttribute)))) - { - if (customAttribute.AttributeType == typeof(DefaultAttribute)) + else if (customAttribute.AttributeType == typeof(DefaultAttribute)) { defaultItem = true; } } - if (statusCode > 0) + if (!applicationIds.Any()) { - if (!_dictionary.ContainsKey(pluginContext)) - { - _dictionary.Add(pluginContext, new StatusPageDictionaryItem()); - } + // no application specified + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:statuspagemanager.applicationless", id) + ); + + break; + } - var item = _dictionary[pluginContext]; - if (!item.ContainsKey(statusCode)) + if (applicationIds.Count() > 1) + { + // too many specified applications + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:statuspagemanager.applicationrich", id) + ); + } + + // assign the module to existing applications. + var applicationContext = _componentManager.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); + + if (statusResponse != default) + { + var stausIcon = !string.IsNullOrEmpty(icon) ? UriResource.Combine(applicationContext.ContextPath, icon) : null; + var statusCode = statusResponse.GetCustomAttribute().StatusCode; + var statusPageContext = new StatusPageContext() { - item.Add(statusCode, new StatusPageItem() - { - Id = id, - StatusCode = statusCode, - StatusPageClass = resource, - PluginContext = pluginContext, - Title = title, - Icon = new UriResource(icon) - }); + StatusId = id, + PluginContext = pluginContext, + ApplicationContext = applicationContext, + StatusCode = statusCode, + StatusTitle = title, + StatusIcon = stausIcon + }; + + if (_dictionary.AddStatusPageItem(pluginContext, applicationContext, statusCode, new StatusPageItem() + { + StatusPageId = id, + StatusPageContext = statusPageContext, + PluginContext = pluginContext, + ApplicationContext = applicationContext, + StatusResponse = statusResponse, + StatusPageClass = resource + })) + { + OnAddStatusPage(statusPageContext); + _httpServerContext.Log.Debug ( I18N.Translate ( "webexpress:statuspagemanager.register", - statusCode, + statusResponse, resource.Name ) ); @@ -157,7 +179,7 @@ public void Register(IPluginContext pluginContext) I18N.Translate ( "webexpress:statuspagemanager.duplicat", - statusCode, + statusResponse, resource.Name ) ); @@ -168,9 +190,18 @@ public void Register(IPluginContext pluginContext) { _defaults.Add(statusCode, new StatusPageItem() { - Id = id, - StatusCode = statusCode, + StatusPageId = id, + StatusPageContext = new StatusPageContext() + { + StatusId = id, + PluginContext = pluginContext, + ApplicationContext = applicationContext, + StatusCode = statusCode, + StatusTitle = title, + StatusIcon = stausIcon + }, StatusPageClass = resource, + StatusResponse = statusResponse, PluginContext = pluginContext }); } @@ -178,11 +209,19 @@ public void Register(IPluginContext pluginContext) { _defaults[statusCode] = new StatusPageItem() { - Id = id, - StatusCode = statusCode, + StatusPageId = id, + StatusPageContext = new StatusPageContext() + { + StatusId = id, + PluginContext = pluginContext, + ApplicationContext = applicationContext, + StatusCode = statusCode, + StatusTitle = title, + StatusIcon = stausIcon + }, StatusPageClass = resource, + StatusResponse = statusResponse, PluginContext = pluginContext, - //ModuleId = moduleId }; } @@ -194,7 +233,8 @@ public void Register(IPluginContext pluginContext) I18N.Translate ( "webexpress:statuspagemanager.statuscode", - resource.Name + resource.Name, + applicationIds.FirstOrDefault() ) ); } @@ -214,94 +254,78 @@ public void Register(IEnumerable pluginContexts) } /// - /// Returns the status codes for a given plugin. + /// Determines the status page for a given application context and status type. /// - /// The context of the plugin. - /// An enumeration of the status codes for the given plugin. - internal IEnumerable GetStatusCodes(IPluginContext pluginContext) + /// The context of the application. + /// The status page class. + /// The context of the status page or null. + public IStatusPageContext GetStatusPage(IApplicationContext applicationContext, Type statusPageClass) { - if (pluginContext == null) - { - return Enumerable.Empty(); - } + var item = _dictionary.GetStatusPageItem(applicationContext, statusPageClass); - if (_dictionary.ContainsKey(pluginContext)) - { - return _dictionary[pluginContext].Keys; - } - - return Enumerable.Empty(); + return item?.StatusPageContext; } /// - /// Returns the class for an status code. + /// Creates a status response. /// + /// The status message. /// The status code. - /// The first status page found to the given states or null. - private StatusPageItem GetStatusPage(int status) + /// The application context where the status pages are located or null for an undefined page (may be from another application) that matches the status code. + /// The request. + /// The response or null. + public Response CreateStatusResponse(string message, int status, IApplicationContext applicationContext, Request request) { - if (_defaults == null) + var statusPageItem = _dictionary.GetStatusPageItem(applicationContext, status); + + if (statusPageItem == null && _defaults.TryGetValue(status, out StatusPageItem value)) { - return null; + statusPageItem = value; } - if (!_defaults.ContainsKey(status)) + if (statusPageItem == null) { - return null; + switch (status) + { + case 401: + return new ResponseUnauthorized() { Content = message }; + case 404: + return new ResponseNotFound() { Content = message }; + case 500: + return new ResponseInternalServerError() { Content = message }; + default: + return new ResponseInternalServerError() { Content = message }; + } } - return _defaults[status]; - } + var instance = ComponentActivator.CreateInstance + ( + statusPageItem.StatusPageClass, + statusPageItem.StatusPageContext, + _componentManager, + new StatusMessage(message) + ); + var type = instance.GetType(); + var renderContext = default(IRenderContext); - /// - /// Returns the class for an status page. - /// - /// The status code. - /// The plugin context where the status pages are located. - /// The first status page found to the given states or null. - private StatusPageItem GetStatusPage(int status, IPluginContext pluginContext) - { - if (pluginContext == null) + if (type.IsGenericType) { - return null; - } + var typeOfT = type.GetGenericArguments()[0]; + var parameters = new object[] { statusPageItem.PluginContext, request, new List() }; - if (!_dictionary.ContainsKey(pluginContext)) - { - return null; + renderContext = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; } - - if (!_dictionary[pluginContext].ContainsKey(status)) + else { - return null; + renderContext = new RenderContext(); } - return _dictionary[pluginContext][status]; - } + instance.Process(renderContext); - /// - /// Creates a status page. - /// - /// The status message. - /// The status code. - /// The module context where the status pages are located or null for an undefined page (may be from another module) that matches the status code. - /// The created status page or null. - public IStatusPage CreateStatusPage(string massage, int status, IPluginContext pluginContext) - { - var responseItem = GetStatusPage(status, pluginContext); - - responseItem ??= GetStatusPage(status); - - if (responseItem == null) - { - return null; - } + var response = Activator.CreateInstance(statusPageItem.StatusResponse) as Response; + response.Content = renderContext.VisualTree.Render(new VisualTreeContext(request)); - var statusPage = responseItem.StatusPageClass.Assembly.CreateInstance(responseItem.StatusPageClass?.FullName) as IStatusPage; - statusPage.StatusMessage = massage; - statusPage.StatusCode = status; - - return statusPage; + return response; } /// @@ -310,14 +334,25 @@ public IStatusPage CreateStatusPage(string massage, int status, IPluginContext p /// The context of the plugin that contains the status pages to remove. public void Remove(IPluginContext pluginContext) { + if (pluginContext == null) + { + return; + } + + // the plugin has not been registered in the manager + if (!_dictionary.ContainsKey(pluginContext)) + { + return; + } + _dictionary.Remove(pluginContext); } /// /// Raises the AddStatusPage event. /// /// The status page. - private void OnAddStatusPage(IPageContext statusPage) + private void OnAddStatusPage(IStatusPageContext statusPage) { AddStatusPage?.Invoke(this, statusPage); } @@ -326,7 +361,7 @@ private void OnAddStatusPage(IPageContext statusPage) /// Raises the RemoveComponent event. /// /// The status page. - private void OnRemoveStatusPage(IPageContext statusPage) + private void OnRemoveStatusPage(IStatusPageContext statusPage) { RemoveStatusPage?.Invoke(this, statusPage); } @@ -339,7 +374,7 @@ private void OnRemoveStatusPage(IPageContext statusPage) /// The shaft deep. public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { - foreach (var statusCode in GetStatusCodes(pluginContext)) + foreach (var statusCode in _dictionary.GetStatusPageContexts(pluginContext).Select(x => x.StatusCode)) { output.Add ( From 7684a18119e37a5d1936bd5c88b20a3c6dc00312 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 12 Oct 2024 08:48:24 +0200 Subject: [PATCH 026/162] bug fixing --- .../WebComponent/ComponentActivator.cs | 76 +++++++++++++++---- .../WebMessage/ResponseBadRequest.cs | 12 ++- .../WebMessage/ResponseForbidden.cs | 12 ++- .../WebMessage/ResponseInternalServerError.cs | 12 ++- .../WebMessage/ResponseNotFound.cs | 13 +++- .../WebMessage/ResponseUnauthorized.cs | 11 +++ .../WebStatusPage/StatusPageManager.cs | 30 +++++--- 7 files changed, 136 insertions(+), 30 deletions(-) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index 83cd8b5..e270982 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using System.Reflection; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebComponent { @@ -10,13 +12,57 @@ namespace WebExpress.WebCore.WebComponent public static class ComponentActivator { /// - /// Creates an instance of the specified component type with the provided context and component manager. + /// Creates an instance of the specified response type with the component hub and advanced parameters. + /// + /// The type of the response. + /// The type of the response to create. + /// The component hub to use for dependency injection. + /// Additional parameter with a status message to pass to the response's constructor. + /// Additional parameters to pass to the component's constructor. + /// An instance of the specified response type. + public static T CreateInstance(Type responseType, IComponentHub componentHub, StatusMessage statusMessage, params object[] advancedParameters) where T : Response + { + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = responseType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(StatusMessage) ? statusMessage : + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return Activator.CreateInstance(responseType) as T; + } + + /// + /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// /// The type of the component, which must implement . /// The type of the component to create. - /// The component manager to use for dependency injection. + /// The component hub to use for dependency injection. + /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, ComponentHub componentManager) where T : class, IComponentManager + public static T CreateInstance(Type componentType, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponentManager { var flags = BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -27,14 +73,16 @@ public static T CreateInstance(Type componentType, ComponentHub componentMana { // injection var parameters = constructor.GetParameters(); - var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var parameterValues = parameters.Select(parameter => - parameter.ParameterType == typeof(IComponentHub) ? componentManager : - parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? - .GetValue(componentManager) ?? null + .GetValue(componentHub) ?? + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null ).ToArray(); if (constructor.Invoke(parameterValues) is T component) @@ -48,16 +96,16 @@ public static T CreateInstance(Type componentType, ComponentHub componentMana } /// - /// Creates an instance of the specified component type with the provided context and component manager. + /// Creates an instance of the specified component type with the provided context and component hub and advanced parameters. /// /// The type of the component, which must implement . /// The type of the context, which must implement . /// The type of the component to create. /// The context to pass to the component's constructor. - /// The component manager to use for dependency injection. + /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, C context, IComponentHub componentManager, params object[] advancedParameters) where T : class, IComponent where C : IContext + public static T CreateInstance(Type componentType, C context, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent where C : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -68,15 +116,15 @@ public static T CreateInstance(Type componentType, C context, IComponentHu { // injection var parameters = constructor.GetParameters(); - var properties = componentManager.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); var parameterValues = parameters.Select(parameter => - parameter.ParameterType == typeof(IComponentHub) ? componentManager : - parameter.ParameterType == typeof(IHttpServerContext) ? componentManager.HttpServerContext : + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : parameter.ParameterType == typeof(C) ? context : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? - .GetValue(componentManager) ?? + .GetValue(componentHub) ?? advancedParameters.Where(x => x.GetType() == parameter.ParameterType) .FirstOrDefault() ?? null ).ToArray(); diff --git a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs index 6f23048..f8b785e 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebMessage { @@ -12,8 +13,17 @@ public class ResponseBadRequest : Response /// Initializes a new instance of the class. /// public ResponseBadRequest() + : this(null) { - var content = "404404 - Bad Request"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseBadRequest(StatusMessage message) + { + var content = message?.Message ?? "404404 - Bad Request"; Reason = "Bad Request"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs index 66debf6..3466864 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebMessage { @@ -12,8 +13,17 @@ public class ResponseForbidden : Response /// Initializes a new instance of the class. /// public ResponseForbidden() + : this(null) { - var content = "403403 - Forbidden"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseForbidden(StatusMessage message) + { + var content = message?.Message ?? "403403 - Forbidden"; Reason = "Forbidden"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs index cc5b704..258c7e1 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebMessage { @@ -12,8 +13,17 @@ public class ResponseInternalServerError : Response /// Initializes a new instance of the class. /// public ResponseInternalServerError() + : this(null) { - var content = "404500 - Internal Server Error"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseInternalServerError(StatusMessage message) + { + var content = message?.Message ?? "404500 - Internal Server Error"; Reason = "Internal Server Error"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs index 9e7a7d0..863e81c 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebMessage { @@ -12,8 +13,18 @@ public class ResponseNotFound : Response /// Initializes a new instance of the class. /// public ResponseNotFound() + : this(null) { - var content = "404404 - Not Found"; + + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseNotFound(StatusMessage message) + { + var content = message?.Message ?? "404404 - Not Found"; Reason = "Not Found"; Header.ContentType = "text/html"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs index f7fe40a..2efe4d9 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; namespace WebExpress.WebCore.WebMessage { @@ -12,6 +13,16 @@ public class ResponseUnauthorized : Response /// Initializes a new instance of the class. /// public ResponseUnauthorized() + : this(null) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseUnauthorized(StatusMessage message) { Reason = "OK"; diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 82bef37..86822ae 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -19,7 +19,7 @@ namespace WebExpress.WebCore.WebStatusPage /// public class StatusPageManager : IStatusPageManager, IComponentManagerPlugin, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly StatusPageDictionary _dictionary = []; private readonly Dictionary _defaults = []; @@ -49,14 +49,14 @@ public class StatusPageManager : IStatusPageManager, IComponentManagerPlugin, IS /// The reference to the context of the host. internal StatusPageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentManager; - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; @@ -134,7 +134,7 @@ public void Register(IPluginContext pluginContext) } // assign the module to existing applications. - var applicationContext = _componentManager.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); + var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); if (statusResponse != default) { @@ -287,14 +287,16 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon { switch (status) { + case 400: + return new ResponseBadRequest(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); case 401: - return new ResponseUnauthorized() { Content = message }; + return new ResponseUnauthorized(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); case 404: - return new ResponseNotFound() { Content = message }; + return new ResponseNotFound(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); case 500: - return new ResponseInternalServerError() { Content = message }; + return new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); default: - return new ResponseInternalServerError() { Content = message }; + return new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); } } @@ -302,7 +304,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon ( statusPageItem.StatusPageClass, statusPageItem.StatusPageContext, - _componentManager, + _componentHub, new StatusMessage(message) ); var type = instance.GetType(); @@ -322,8 +324,12 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon instance.Process(renderContext); - var response = Activator.CreateInstance(statusPageItem.StatusResponse) as Response; - response.Content = renderContext.VisualTree.Render(new VisualTreeContext(request)); + + var response = ComponentActivator.CreateInstance(statusPageItem.StatusResponse, _componentHub, new StatusMessage(message)); + var content = renderContext.VisualTree.Render(new VisualTreeContext(request))?.ToString(); + + response.Content = content; + response.Header.ContentLength = content?.Length ?? 0; return response; } From 69ce7239118bebf10b0cbc317b164124fdbd713c Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 12 Oct 2024 08:50:21 +0200 Subject: [PATCH 027/162] refactor sessionmanager and add tests - added unit tests to ensure reliability and correctness. - refactored core logic for better readability and efficiency. - fixed existing bugs. --- .../Fixture/UnitTestControlFixture.cs | 28 +++--- .../Manager/UnitTestApplication.cs | 22 ++--- .../Manager/UnitTestComponent.cs | 6 +- .../Manager/UnitTestInternationalization.cs | 10 +- .../Manager/UnitTestModule.cs | 22 ++--- .../Manager/UnitTestPageManager.cs | 14 +-- .../Manager/UnitTestPlugin.cs | 28 +++--- .../Manager/UnitTestResourceManager.cs | 12 +-- .../Manager/UnitTestRestApiManager.cs | 14 +-- .../Manager/UnitTestSessionManager.cs | 98 +++++++++++++++++++ .../Manager/UnitTestSitemapManager.cs | 12 +-- .../Manager/UnitTestStatusPageManager.cs | 31 +++--- .../Message/UnitTestGetRequest.cs | 10 +- .../Schedule/UnitTestCron.cs | 18 ++-- .../WebSession/AuthorizationService.cs | 10 +- .../WebSession/Model/Session.cs | 5 +- .../Model/SessionPropertyAuthentification.cs | 3 + .../Model/SessionPropertyAuthorization.cs | 3 + .../Model/SessionPropertyParameter.cs | 15 ++- .../WebSession/SessionManager.cs | 39 +++----- 20 files changed, 250 insertions(+), 150 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index f12a2e7..e4f2dd8 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -29,10 +29,10 @@ public UnitTestControlFixture() } /// - /// Create a a server context. + /// Create a fake server context. /// /// The server context. - public static IHttpServerContext CreateHttpServerContext() + public static IHttpServerContext CreateHttpServerContextMock() { return new HttpServerContext ( @@ -53,7 +53,7 @@ public static IHttpServerContext CreateHttpServerContext() /// Create a component hub. ///
/// The component manager. - public static ComponentHub CreateComponentHub() + public static ComponentHub CreateComponentHubMock() { var ctorComponentManager = typeof(ComponentHub).GetConstructor ( @@ -63,7 +63,7 @@ public static ComponentHub CreateComponentHub() null ); - var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContext()]); + var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContextMock()]); // set static field in the webex class var type = typeof(WebEx); @@ -78,9 +78,9 @@ public static ComponentHub CreateComponentHub() /// Create a component hub and register the plugins. ///
/// The component manager. - public static ComponentHub CreateAndRegisterComponentHub() + public static ComponentHub CreateAndRegisterComponentHubMock() { - var componentManager = CreateComponentHub(); + var componentManager = CreateComponentHubMock(); var pluginManager = componentManager.PluginManager as PluginManager; pluginManager.Register(); @@ -93,9 +93,9 @@ public static ComponentHub CreateAndRegisterComponentHub() ///
/// The content. /// A fake request for testing. - public static WebMessage.Request CrerateRequest(string content = "") + public static WebMessage.Request CrerateRequestMock(string content = "") { - var context = CreateHttpContext(content); + var context = CreateHttpContextMock(content); return context.Request; } @@ -105,7 +105,7 @@ public static WebMessage.Request CrerateRequest(string content = "") ///
/// The content. /// A fake http context for testing. - public static WebMessage.HttpContext CreateHttpContext(string content = "") + public static WebMessage.HttpContext CreateHttpContextMock(string content = "") { var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); var featureCollection = new FeatureCollection(); @@ -165,7 +165,7 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") featureCollection.Set(requestIdentifierFeature); featureCollection.Set(connectionFeature); - var componentManager = CreateComponentHub(); + var componentManager = CreateComponentHubMock(); var context = new WebMessage.HttpContext(featureCollection, componentManager.HttpServerContext); return context; @@ -175,18 +175,18 @@ public static WebMessage.HttpContext CreateHttpContext(string content = "") /// Create a fake render context. ///
/// A fake context for testing. - public static RenderContext CrerateContext() + public static RenderContext CrerateContextMock() { - var request = CrerateRequest(); + var request = CrerateRequestMock(); - return new RenderContext(CreratePageContext()?.ModuleContext?.ApplicationContext, request, []); + return new RenderContext(CreratePageContextMock()?.ModuleContext?.ApplicationContext, request, []); } /// /// Create a fake page context. /// /// A fake context for testing. - public static PageContext CreratePageContext() + public static PageContext CreratePageContextMock() { var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index 5545172..ff13d0e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -18,7 +18,7 @@ public class UnitTestApplication public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -37,7 +37,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var applicationManager = componentHub.ApplicationManager as ApplicationManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -57,7 +57,7 @@ public void Remove() public void Id(Type applicationType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -74,7 +74,7 @@ public void Id(Type applicationType, string id) public void Name(Type applicationType, string name) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -91,7 +91,7 @@ public void Name(Type applicationType, string name) public void Description(Type applicationType, string description) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -108,7 +108,7 @@ public void Description(Type applicationType, string description) public void Icon(Type applicationType, string icon) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -125,7 +125,7 @@ public void Icon(Type applicationType, string icon) public void ContextPath(Type applicationType, string contextPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -142,7 +142,7 @@ public void ContextPath(Type applicationType, string contextPath) public void AssetPath(Type applicationType, string assetPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -159,7 +159,7 @@ public void AssetPath(Type applicationType, string assetPath) public void DataPath(Type applicationType, string dataPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); // test execution @@ -173,7 +173,7 @@ public void DataPath(Type applicationType, string dataPath) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ApplicationManager.GetType())); @@ -186,7 +186,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.ApplicationManager.Applications) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs index b01ab3d..79fe791 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs @@ -15,7 +15,7 @@ public class UnitTestComponent public void PluginManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); // test execution Assert.NotNull(componentHub.PluginManager); @@ -28,7 +28,7 @@ public void PluginManager() public void ApplicationManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); // test execution Assert.NotNull(componentHub.ApplicationManager); @@ -41,7 +41,7 @@ public void ApplicationManager() public void ModuleManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); // test execution Assert.NotNull(componentHub.ModuleManager); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 2c57027..6b14444 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -19,7 +19,7 @@ public class UnitTestInternationalization public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -35,7 +35,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var internationalizationManager = componentHub.InternationalizationManager as InternationalizationManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -52,7 +52,7 @@ public void Remove() public void GetDefaultCulture() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); @@ -72,7 +72,7 @@ public void GetDefaultCulture() public void Translate(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions - UnitTestControlFixture.CreateAndRegisterComponentHub(); + UnitTestControlFixture.CreateAndRegisterComponentHubMock(); if (cultureName == null && !param.Any()) { @@ -126,7 +126,7 @@ public void Translate(string key, string excepted, string cultureName = null, st public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.InternationalizationManager.GetType())); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs index 438e428..be60d4a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -18,7 +18,7 @@ public class UnitTestModule public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -34,7 +34,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var moduleManager = componentHub.ModuleManager as ModuleManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -54,7 +54,7 @@ public void Remove() public void Id(Type applicationType, Type moduleType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -73,7 +73,7 @@ public void Id(Type applicationType, Type moduleType, string id) public void Name(Type applicationType, Type moduleType, string name) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -92,7 +92,7 @@ public void Name(Type applicationType, Type moduleType, string name) public void Description(Type applicationType, Type moduleType, string description) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -113,7 +113,7 @@ public void Description(Type applicationType, Type moduleType, string descriptio public void Icon(Type applicationType, Type moduleType, string icon) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -134,7 +134,7 @@ public void Icon(Type applicationType, Type moduleType, string icon) public void ContextPath(Type applicationType, Type moduleType, string contextPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -155,7 +155,7 @@ public void ContextPath(Type applicationType, Type moduleType, string contextPat public void AssetPath(Type applicationType, Type moduleType, string assetPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -176,7 +176,7 @@ public void AssetPath(Type applicationType, Type moduleType, string assetPath) public void DataPath(Type applicationType, Type moduleType, string dataPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); // test execution @@ -190,7 +190,7 @@ public void DataPath(Type applicationType, Type moduleType, string dataPath) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ModuleManager.GetType())); @@ -203,7 +203,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var module in componentHub.ModuleManager.Modules) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index b00698b..b4080d8 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -17,7 +17,7 @@ public class UnitTestPageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(3, componentHub.PageManager.Pages.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var pageManager = componentHub.PageManager as PageManager; @@ -50,7 +50,7 @@ public void Remove() public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var page = componentHub.PageManager.GetPage(module, resourceType); @@ -69,7 +69,7 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string public void Title(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var page = componentHub.PageManager.GetPage(module, resourceType); @@ -87,7 +87,7 @@ public void Title(Type applicationType, Type moduleType, Type resourceType, stri public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var page = componentHub.PageManager.GetPage(module, resourceType); @@ -102,7 +102,7 @@ public void ContextPath(Type applicationType, Type moduleType, Type resourceType public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.PageManager.GetType())); @@ -115,7 +115,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.PageManager.Pages) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index b198524..42f6140 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -17,7 +17,7 @@ public class UnitTestPlugin public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -34,7 +34,7 @@ public void Register() public void RegisterEvent() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; var i = 0; var triggered = false; @@ -57,7 +57,7 @@ public void RegisterEvent() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -75,7 +75,7 @@ public void Remove() public void RemoveEvent() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; var i = 1; var triggered = false; @@ -99,7 +99,7 @@ public void RemoveEvent() public void GetPluginById() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution var plugin = componentHub.PluginManager.GetPlugin("webexpress.webcore.test"); @@ -114,7 +114,7 @@ public void GetPluginById() public void GetPluginByType() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -129,7 +129,7 @@ public void GetPluginByType() public void Id() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -143,7 +143,7 @@ public void Id() public void GetName() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -157,7 +157,7 @@ public void GetName() public void GetDescription() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -171,7 +171,7 @@ public void GetDescription() public void GetIcon() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -189,7 +189,7 @@ public void GetIcon() public void Boot(string pluginId, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(pluginId); @@ -212,7 +212,7 @@ public void Boot(string pluginId, string expected) public void ShutDown(string pluginId, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHub(); + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(pluginId); @@ -231,7 +231,7 @@ public void ShutDown(string pluginId, string expected) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -245,7 +245,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var plugin in componentHub.PluginManager.Plugins) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index 3ce93e7..a827d4d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -17,7 +17,7 @@ public class UnitTestResourceManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution // resources (3 unique + 2 ambiguous) @@ -31,7 +31,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var resourceManager = componentHub.ResourceManager as ResourceManager; @@ -55,7 +55,7 @@ public void Remove() public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var resource = componentHub.ResourceManager.GetResorce(module, resourceType); @@ -76,7 +76,7 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var resource = componentHub.ResourceManager.GetResorce(module, resourceType); @@ -91,7 +91,7 @@ public void ContextPath(Type applicationType, Type moduleType, Type resourceType public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); @@ -104,7 +104,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.ResourceManager.Resources) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index ef37257..16679f5 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -17,7 +17,7 @@ public class UnitTestRestApiManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(3, componentHub.RestApiManager.RestApis.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var apiManager = componentHub.RestApiManager as RestApiManager; @@ -50,7 +50,7 @@ public void Remove() public void Id(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var api = componentHub.RestApiManager.GetRestApi(module, resourceType); @@ -68,7 +68,7 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var api = componentHub.RestApiManager.GetRestApi(module, resourceType); @@ -87,7 +87,7 @@ public void ContextPath(Type applicationType, Type moduleType, Type resourceType public void Method(Type applicationType, Type moduleType, Type resourceType, CrudMethod method) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); var api = componentHub.RestApiManager.GetRestApi(module, resourceType); @@ -102,7 +102,7 @@ public void Method(Type applicationType, Type moduleType, Type resourceType, Cru public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.RestApiManager.GetType())); @@ -115,7 +115,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var api in componentHub.RestApiManager.RestApis) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs new file mode 100644 index 0000000..fc00c0c --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs @@ -0,0 +1,98 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebSession.Model; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the session manager. + /// + [Collection("NonParallelTests")] + public class UnitTestSessionManager + { + /// + /// Test the register function of the session manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.NotNull(componentHub.SessionManager); + } + + /// + /// Tests whether the session manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SessionManager.GetType())); + } + + /// + /// Test the GetSession function of the session manager. + /// + [Fact] + public void GetSession() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestControlFixture.CrerateRequestMock(); + + // test execution + var session = componentHub.SessionManager.GetSession(request); + + Assert.NotNull(session); + } + + /// + /// Test the SetProperty function of the session manager. + /// + [Fact] + public void AddPropertyToSession() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestControlFixture.CrerateRequestMock(); + var session = componentHub.SessionManager.GetSession(request); + + // test execution + session.SetProperty(new SessionPropertyParameter(new Parameter("test", "test param", ParameterScope.Session))); + + var testProperty = session.GetProperty(); + + Assert.NotNull(testProperty); + Assert.Single(testProperty.Params); + Assert.Equal("test param", testProperty.Params["test"].Value); + Assert.Equal(ParameterScope.Session, testProperty.Params["test"].Scope); + } + + /// + /// Test the RemoveProperty function of the session manager. + /// + [Fact] + public void RemovePropertyFromSession() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestControlFixture.CrerateRequestMock(); + var session = componentHub.SessionManager.GetSession(request); + + // test execution + session.SetProperty(new SessionPropertyParameter(new Parameter("test", "test param", ParameterScope.Session))); + session.RemoveProperty(); + + var testProperty = session.GetProperty(); + + Assert.Null(testProperty); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index d2e07a8..b75ee30 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -17,7 +17,7 @@ public class UnitTestSitemapManager public void Refresh() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution componentManager.SitemapManager.Refresh(); @@ -43,8 +43,8 @@ public void Refresh() public void SearchResource(string uri, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); - var context = UnitTestControlFixture.CreateHttpContext(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CreateHttpContextMock(); componentHub.SitemapManager.Refresh(); // test execution @@ -55,7 +55,7 @@ public void SearchResource(string uri, string id) HttpContext = context }); - searchResult.Process(UnitTestControlFixture.CrerateRequest()); + searchResult.Process(UnitTestControlFixture.CrerateRequestMock()); Assert.Equal(id, searchResult.EndpointId); } @@ -75,7 +75,7 @@ public void SearchResource(string uri, string id) public void GetUri(Type resourceType, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); componentHub.SitemapManager.Refresh(); // test execution @@ -91,7 +91,7 @@ public void GetUri(Type resourceType, string expected) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SitemapManager.GetType())); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index bb406ae..01d61f6 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -17,7 +17,7 @@ public class UnitTestStatusPageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(3, componentHub.StatusPageManager.StatusPages.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var statusPageManager = componentHub.StatusPageManager as StatusPageManager; @@ -50,7 +50,7 @@ public void Remove() public void Id(Type applicationType, Type statusPageType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -67,7 +67,7 @@ public void Id(Type applicationType, Type statusPageType, string id) public void Title(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, resourceType); @@ -87,7 +87,7 @@ public void Title(Type applicationType, Type resourceType, string id) public void Code(Type applicationType, Type statusPageType, int? code) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -107,7 +107,7 @@ public void Code(Type applicationType, Type statusPageType, int? code) public void Icon(Type applicationType, Type statusPageType, string icon) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -127,9 +127,9 @@ public void Icon(Type applicationType, Type statusPageType, string icon) public void CreateAndCheckCode(Type applicationType, int statusCode, int? expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); - var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestControlFixture.CreateHttpContext().Request); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); // test execution Assert.Equal(expected, statusResponse?.Status); @@ -139,17 +139,18 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect /// Test the icon property of the status page. ///
[Theory] - [InlineData(typeof(TestApplicationA), 400, "content", "content")] - [InlineData(typeof(TestApplicationA), 500, "content", "content")] - public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected) + [InlineData(typeof(TestApplicationA), 400, "content", "content", 78)] + [InlineData(typeof(TestApplicationA), 500, "content", "content", 7)] + public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected, int length) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(applicationType); - var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestControlFixture.CreateHttpContext().Request); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); // test execution Assert.Contains(expected, statusResponse?.Content?.ToString()); + Assert.Equal(length, statusResponse?.Header?.ContentLength); } /// @@ -159,7 +160,7 @@ public void CreateAndCheckMessage(Type applicationType, int statusCode, string c public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.StatusPageManager.GetType())); @@ -172,7 +173,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHub(); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.StatusPageManager.StatusPages) diff --git a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs index 34ec888..eb79278 100644 --- a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs +++ b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs @@ -15,7 +15,7 @@ public class UnitTestGetRequest public void General() { var content = UnitTestControlFixture.GetEmbeddedResource("general.get"); - var request = UnitTestControlFixture.CrerateRequest(content); + var request = UnitTestControlFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -27,7 +27,7 @@ public void General() public void Less() { var content = UnitTestControlFixture.GetEmbeddedResource("less.get"); - var request = UnitTestControlFixture.CrerateRequest(content); + var request = UnitTestControlFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -39,7 +39,7 @@ public void Less() public void Massive() { var content = UnitTestControlFixture.GetEmbeddedResource("massive.get"); - var request = UnitTestControlFixture.CrerateRequest(content); + var request = UnitTestControlFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -51,7 +51,7 @@ public void Massive() public void GetParameter() { var content = UnitTestControlFixture.GetEmbeddedResource("param.get"); - var request = UnitTestControlFixture.CrerateRequest(content); + var request = UnitTestControlFixture.CrerateRequestMock(content); var param = request?.GetParameter("a")?.Value; Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); @@ -65,7 +65,7 @@ public void GetParameter() public void GetParameterWithUmlaut() { var content = UnitTestControlFixture.GetEmbeddedResource("param_umlaut.get"); - var request = UnitTestControlFixture.CrerateRequest(content); + var request = UnitTestControlFixture.CrerateRequestMock(content); var a = request?.GetParameter("a")?.Value; var b = request?.GetParameter("b")?.Value; diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs index f25cbda..3988fce 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs @@ -14,7 +14,7 @@ public class UnitTestCron public void Create_1() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var clock = new Clock(); var cron = new Cron(context, "0-59", "*", "1-31", "1-2,3,4,5,6,7,8-10,11,12"); @@ -26,7 +26,7 @@ public void Create_1() public void Create_2() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 1, dateTime.Day, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "0-33", "2, 1-4, x"); @@ -39,7 +39,7 @@ public void Create_2() public void Create_3() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "12"); @@ -52,7 +52,7 @@ public void Create_3() public void Create_4() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; Log.Current.Clear(); @@ -67,7 +67,7 @@ public void Create_4() public void Create_5() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "*", ""); @@ -80,7 +80,7 @@ public void Create_5() public void Create_6() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; Log.Current.Clear(); @@ -95,7 +95,7 @@ public void Create_6() public void Matching_1() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "1-11"); @@ -108,7 +108,7 @@ public void Matching_1() public void Matching_2() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "3"); // wednesday @@ -121,7 +121,7 @@ public void Matching_2() public void Matching_3() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContext(); + var context = UnitTestControlFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "1"); // sunday diff --git a/src/WebExpress.WebCore/WebSession/AuthorizationService.cs b/src/WebExpress.WebCore/WebSession/AuthorizationService.cs index 509d8b8..5818224 100644 --- a/src/WebExpress.WebCore/WebSession/AuthorizationService.cs +++ b/src/WebExpress.WebCore/WebSession/AuthorizationService.cs @@ -2,13 +2,17 @@ namespace WebExpress.WebCore.WebSession { + + /// + /// Represents an abstract authorization service. + /// public abstract class AuthorizationService { /// - /// Prüft ob der authentifizierte Nutzer autorisiert ist + /// Checks if the authenticated user is authorized. /// - /// Die aktuelle Session - /// true, wenn autorisiert false sonst + /// The current session. + /// true if authorized, false otherwise. public abstract bool Authorization(Session session); } } diff --git a/src/WebExpress.WebCore/WebSession/Model/Session.cs b/src/WebExpress.WebCore/WebSession/Model/Session.cs index 300d186..ad2e3f7 100644 --- a/src/WebExpress.WebCore/WebSession/Model/Session.cs +++ b/src/WebExpress.WebCore/WebSession/Model/Session.cs @@ -115,10 +115,7 @@ public void SetProperty(ISessionProperty property) { lock (Properties) { - if (Properties.ContainsKey(typeof(T))) - { - Properties.Remove(typeof(T)); - } + Properties.Remove(typeof(T)); } } diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs index 32da55e..ccb0252 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.WebSession.Model { + /// + /// Represents the authentication session property. + /// public class SessionPropertyAuthentification : SessionProperty { /// diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs index ece2312..af271b1 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.WebSession.Model { + /// + /// Represents a session property authorization. + /// public class SessionPropertyAuthorization : SessionProperty { } diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs index 53e7c0b..6080c9d 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs @@ -3,28 +3,33 @@ namespace WebExpress.WebCore.WebSession.Model { + /// + /// Represents a session property with parameters. + /// public class SessionPropertyParameter : SessionProperty { /// /// Returns the parameters. /// - public Dictionary Params { get; private set; } + public Dictionary Params { get; } = []; /// /// Initializes a new instance of the class. /// public SessionPropertyParameter() { - Params = new Dictionary(); } /// /// Initializes a new instance of the class. /// - /// The parameters - public SessionPropertyParameter(Dictionary param) + /// The parameters + public SessionPropertyParameter(params Parameter[] parameters) { - Params = param; + foreach (var param in parameters) + { + Params.Add(param.Key, param); + } } } } diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 3513cb3..8654edf 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -9,41 +9,30 @@ namespace WebExpress.WebCore.WebSession { + /// + /// Represents a session manager that handles session creation and retrieval. + /// public class SessionManager : IComponentManager, ISystemComponent { - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - - /// - /// Returns the directory in which the sessions are stored on the server side. - /// - private SessionDictionary Dictionary { get; } = new SessionDictionary(); + private readonly IHttpServerContext _httpServerContext; + private readonly SessionDictionary _dictionary = []; /// /// Initializes a new instance of the class. /// - internal SessionManager() - { - } - - /// - /// Initialization - /// /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) + internal SessionManager(IHttpServerContext context) { - HttpServerContext = context; + _httpServerContext = context; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:sessionmanager.initialization") ); } /// - /// Creates a session or returns an existing session. + /// Creates a session or returns an existing session based on the provided request. /// /// The request. /// The session. @@ -67,9 +56,9 @@ public Session GetSession(Request request) } - if (sessionCookie != null && Dictionary.ContainsKey(Guid)) + if (sessionCookie != null && _dictionary.ContainsKey(Guid)) { - session = Dictionary[Guid]; + session = _dictionary[Guid]; session.Updated = DateTime.Now; } else @@ -77,9 +66,9 @@ public Session GetSession(Request request) // no or invalid session => assign new session session = new Session(Guid); - lock (Dictionary) + lock (_dictionary) { - Dictionary[Guid] = session; + _dictionary[Guid] = session; } } @@ -87,7 +76,7 @@ public Session GetSession(Request request) } /// - /// Information about the component is collected and prepared for output in the log. + /// Collects and prepares information about the component for output in the log. /// /// The context of the plugin. /// A list of log entries. From 7b92b7b1aa4d01027eb01a774e700d76be092e4a Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 12 Oct 2024 09:47:55 +0200 Subject: [PATCH 028/162] refactoring session manager --- .../WebComponent/ComponentHub.cs | 5 +++-- .../WebComponent/IComponentHub.cs | 2 +- .../WebSession/ISessionManager.cs | 19 +++++++++++++++++++ .../WebSession/SessionManager.cs | 2 +- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 src/WebExpress.WebCore/WebSession/ISessionManager.cs diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index be67f2a..546f943 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -35,6 +35,7 @@ public class ComponentHub : IComponentHub private readonly RestApiManager _restApiManager; private readonly SitemapManager _sitemapManager; private readonly StatusPageManager _statusPageManager; + private readonly SessionManager _sessionManager; /// /// An event that fires when an component is added. @@ -155,7 +156,7 @@ public class ComponentHub : IComponentHub /// Returns the session manager. /// /// The instance of the session manager. - public SessionManager SessionManager { get; private set; } + public ISessionManager SessionManager => _sessionManager; /// /// Returns the task manager. @@ -185,7 +186,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _statusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; EventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; - SessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; + _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; _internationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 212e8d5..691d148 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -124,7 +124,7 @@ public interface IComponentHub : IComponentManager /// Returns the session manager. /// /// The instance of the session manager. - SessionManager SessionManager { get; } + ISessionManager SessionManager { get; } /// /// Returns the task manager. diff --git a/src/WebExpress.WebCore/WebSession/ISessionManager.cs b/src/WebExpress.WebCore/WebSession/ISessionManager.cs new file mode 100644 index 0000000..7f81cbf --- /dev/null +++ b/src/WebExpress.WebCore/WebSession/ISessionManager.cs @@ -0,0 +1,19 @@ +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebSession.Model; + +namespace WebExpress.WebCore.WebSession +{ + /// + /// Represents a session manager that handles session creation and retrieval. + /// + public interface ISessionManager : IComponentManager + { + /// + /// Creates a session or returns an existing session based on the provided request. + /// + /// The request. + /// The session. + Session GetSession(Request request); + } +} diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 8654edf..0bde8e6 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.WebSession /// /// Represents a session manager that handles session creation and retrieval. /// - public class SessionManager : IComponentManager, ISystemComponent + public class SessionManager : ISessionManager, ISystemComponent { private readonly IHttpServerContext _httpServerContext; private readonly SessionDictionary _dictionary = []; From 9ee61fa0c351d79042d380d2246ed42afe67558e Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 13 Oct 2024 19:01:30 +0200 Subject: [PATCH 029/162] restructured event manager and added tests - refactored event manager for improved readability and maintainability. - added unit tests to cover all major functionalities. --- .../Manager/UnitTestEventManager.cs | 144 ++++++++++ .../Manager/UnitTestModule.cs | 3 - src/WebExpress.WebCore.Test/TestEventA.cs | 11 + .../TestEventArgument.cs | 15 ++ src/WebExpress.WebCore.Test/TestEventB.cs | 11 + .../TestEventHandlerA1.cs | 67 +++++ .../TestEventHandlerB1.cs | 64 +++++ src/WebExpress.WebCore.Test/TestModuleAB1.cs | 2 +- src/WebExpress.WebCore.Test/TestModuleB1.cs | 2 +- src/WebExpress.WebCore.Test/TestModuleC1.cs | 2 +- src/WebExpress.WebCore.Test/TestModuleC2.cs | 2 +- src/WebExpress.WebCore.Test/TestPageA1Z.cs | 2 +- .../Internationalization/de | 5 + .../Internationalization/en | 5 + .../WebApplication/ApplicationManager.cs | 10 + .../WebApplication/IApplicationManager.cs | 7 + .../WebAttribute/ApplicationAttribute.cs | 4 +- .../WebAttribute/EventAttribute.cs | 21 ++ .../WebAttribute/IEventAttribute.cs | 9 + .../WebComponent/ComponentActivator.cs | 44 ++++ .../WebComponent/ComponentHub.cs | 9 +- .../WebComponent/IComponent.cs | 6 +- .../WebComponent/IComponentHub.cs | 2 +- .../WebEvent/EventHandlerContext.cs | 31 +++ .../WebEvent/EventManager.cs | 245 ++++++++++++++---- src/WebExpress.WebCore/WebEvent/IEvent.cs | 9 + .../WebEvent/IEventArgument.cs | 9 + .../WebEvent/IEventContext.cs | 18 -- .../WebEvent/IEventHandler.cs | 28 +- .../WebEvent/IEventHandlerContext.cs | 32 +++ .../WebEvent/IEventManager.cs | 43 +++ .../WebEvent/Model/EventDictionary.cs | 148 ++++++++++- .../WebEvent/Model/EventItem.cs | 122 +++++++++ .../WebPackage/Model/PackageItem.cs | 9 + src/WebExpress.WebCore/WebPage/Page.cs | 5 + .../WebResource/Resource.cs | 52 +--- .../WebResource/ResourceAsset.cs | 9 + .../WebResource/ResourceBinary.cs | 4 +- .../WebResource/ResourceFile.cs | 13 +- .../WebResource/ResourceRest.cs | 84 ------ src/WebExpress.WebCore/WebRestAPI/RestApi.cs | 8 + .../Model/StatusPageDictionary.cs | 6 +- .../WebStatusPage/Model/StatusPageItem.cs | 9 + .../WebStatusPage/StatusPageManager.cs | 8 + 44 files changed, 1101 insertions(+), 238 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestEventA.cs create mode 100644 src/WebExpress.WebCore.Test/TestEventArgument.cs create mode 100644 src/WebExpress.WebCore.Test/TestEventB.cs create mode 100644 src/WebExpress.WebCore.Test/TestEventHandlerA1.cs create mode 100644 src/WebExpress.WebCore.Test/TestEventHandlerB1.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/EventAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IEventAttribute.cs create mode 100644 src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs create mode 100644 src/WebExpress.WebCore/WebEvent/IEvent.cs create mode 100644 src/WebExpress.WebCore/WebEvent/IEventArgument.cs delete mode 100644 src/WebExpress.WebCore/WebEvent/IEventContext.cs create mode 100644 src/WebExpress.WebCore/WebEvent/IEventHandlerContext.cs create mode 100644 src/WebExpress.WebCore/WebEvent/IEventManager.cs create mode 100644 src/WebExpress.WebCore/WebEvent/Model/EventItem.cs delete mode 100644 src/WebExpress.WebCore/WebResource/ResourceRest.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs new file mode 100644 index 0000000..1800c83 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs @@ -0,0 +1,144 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the event manager. + /// + [Collection("NonParallelTests")] + public class UnitTestEventManager + { + /// + /// Test the register function of the event manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(2, componentHub.EventManager.EventHandlers.Count()); + } + + /// + /// Test the remove function of the event manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var eventManager = componentHub.EventManager as EventManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + eventManager.Remove(plugin); + + Assert.Empty(componentHub.EventManager.EventHandlers); + } + + /// + /// Tests whether the event manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.EventManager.GetType())); + } + + /// + /// Test the id property of the event handler. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestEventA), "webexpress.webcore.test.testeventhandlera1")] + [InlineData(typeof(TestApplicationB), typeof(TestEventB), "webexpress.webcore.test.testeventhandlerb1")] + [InlineData(typeof(TestApplicationA), typeof(TestEventB), null)] + [InlineData(typeof(TestApplicationB), typeof(TestEventA), null)] + public void Id(Type applicationType, Type eventType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + + // test execution + var eventHandlers = componentHub.EventManager.GetEventHandlers(application, eventType); + + if (id == null) + { + Assert.Empty(eventHandlers); + return; + } + + Assert.Contains(id, eventHandlers.Select(x => x.EventHandlerId)); + } + + /// + /// Test the id property of the event handler. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestEventA), "webexpress.webcore.test.testeventa")] + [InlineData(typeof(TestApplicationB), typeof(TestEventB), "webexpress.webcore.test.testeventb")] + [InlineData(typeof(TestApplicationA), typeof(TestEventB), null)] + [InlineData(typeof(TestApplicationB), typeof(TestEventA), null)] + public void EventId(Type applicationType, Type eventType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplication(applicationType); + + // test execution + var eventHandlers = componentHub.EventManager.GetEventHandlers(application, eventType); + + if (id == null) + { + Assert.Empty(eventHandlers); + return; + } + + Assert.Contains(id, eventHandlers?.Select(x => x.EventId)); + } + + /// + /// Test raise the event handler. + /// + [Fact] + public void RaiseEventA1() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplication(); + + // test execution + var eventArgument = new TestEventArgument() { TestProperty = false }; + + componentHub.EventManager.RaiseEvent(application, this, eventArgument); + + Assert.True(eventArgument.TestProperty); + } + + /// + /// Test raise the event handler. + /// + [Fact] + public void RaiseEventB1() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplication(); + + // test execution + var eventArgument = new TestEventArgument() { TestProperty = false }; + + componentHub.EventManager.RaiseEvent(application, this, eventArgument); + + Assert.True(eventArgument.TestProperty); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs index be60d4a..5dd1064 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs @@ -55,7 +55,6 @@ public void Id(Type applicationType, Type moduleType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); @@ -74,7 +73,6 @@ public void Name(Type applicationType, Type moduleType, string name) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); @@ -93,7 +91,6 @@ public void Description(Type applicationType, Type moduleType, string descriptio { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); diff --git a/src/WebExpress.WebCore.Test/TestEventA.cs b/src/WebExpress.WebCore.Test/TestEventA.cs new file mode 100644 index 0000000..788746f --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestEventA.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test event. + /// + internal class TestEventA : IEvent + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestEventArgument.cs b/src/WebExpress.WebCore.Test/TestEventArgument.cs new file mode 100644 index 0000000..eb5c2f4 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestEventArgument.cs @@ -0,0 +1,15 @@ +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test event argument. + /// + public class TestEventArgument : IEventArgument + { + /// + /// Returns or sets a boolean value for testing purposes. + /// + public bool TestProperty { get; set; } + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/TestEventB.cs b/src/WebExpress.WebCore.Test/TestEventB.cs new file mode 100644 index 0000000..2b20742 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestEventB.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test event. + /// + internal class TestEventB : IEvent + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestEventHandlerA1.cs b/src/WebExpress.WebCore.Test/TestEventHandlerA1.cs new file mode 100644 index 0000000..972f720 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestEventHandlerA1.cs @@ -0,0 +1,67 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy event handler for testing purposes. + /// + [Application()] + [Event] + public sealed class TestEventHandlerA1 : IEventHandler + { + /// + /// Initialization of the event. + /// + /// The event handler context, for testing the injection. + /// The application manager, for testing the injection. + private TestEventHandlerA1(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) + { + // test the injection + if (eventHandlerContext == null) + { + throw new ArgumentNullException(nameof(eventHandlerContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Process the event. + /// + /// The object that triggered the event. + /// The argument for the event. + public void Process(object sender, IEventArgument eventArgument) + { + // test the parameter + if (sender == null) + { + throw new ArgumentNullException(nameof(sender), "Parameter cannot be null or empty."); + } + + // test the parameter + if (eventArgument == null) + { + throw new ArgumentNullException(nameof(eventArgument), "Parameter cannot be null or empty."); + } + + // to check in the test whether the handler has been executed correctly + if (eventArgument is TestEventArgument arg) + { + arg.TestProperty = true; + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestEventHandlerB1.cs b/src/WebExpress.WebCore.Test/TestEventHandlerB1.cs new file mode 100644 index 0000000..c8767cf --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestEventHandlerB1.cs @@ -0,0 +1,64 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy event handler for testing purposes. + /// + [Application()] + [Event] + public sealed class TestEventHandlerB1 : IEventHandler + { + /// + /// Initialization of the event. + /// + /// The event handler context, for testing the injection. + /// The application manager, for testing the injection. + private TestEventHandlerB1(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) + { + // test the injection + if (eventHandlerContext == null) + { + throw new ArgumentNullException(nameof(eventHandlerContext), "Parameter cannot be null or empty."); + } + + // test the injection + if (applicationManager == null) + { + throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); + } + } + + /// + /// Process the event. + /// + /// The object that triggered the event. + /// The argument for the event. + public void Process(object sender, TestEventArgument eventArgument) + { + // test the parameter + if (sender == null) + { + throw new ArgumentNullException(nameof(sender), "Parameter cannot be null or empty."); + } + + // test the parameter + if (eventArgument == null) + { + throw new ArgumentNullException(nameof(eventArgument), "Parameter cannot be null or empty."); + } + + // to check in the test whether the handler has been executed correctly + eventArgument.TestProperty = true; + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestModuleAB1.cs b/src/WebExpress.WebCore.Test/TestModuleAB1.cs index 5e5e289..cb216b3 100644 --- a/src/WebExpress.WebCore.Test/TestModuleAB1.cs +++ b/src/WebExpress.WebCore.Test/TestModuleAB1.cs @@ -18,7 +18,7 @@ public sealed class TestModuleAB1 : IModule /// Initialization of the module. /// /// The module context, for testing the injection. - /// The application manager, for testing the injection. + /// The application manager, for testing the injection. private TestModuleAB1(IModuleContext moduleContext, ApplicationManager applicationManager) { // test the injection diff --git a/src/WebExpress.WebCore.Test/TestModuleB1.cs b/src/WebExpress.WebCore.Test/TestModuleB1.cs index dee725b..5f9cd0a 100644 --- a/src/WebExpress.WebCore.Test/TestModuleB1.cs +++ b/src/WebExpress.WebCore.Test/TestModuleB1.cs @@ -17,7 +17,7 @@ public sealed class TestModuleB1 : IModule /// Initialization of the module. /// /// The module context, for testing the injection. - /// The application manager, for testing the injection. + /// The application manager, for testing the injection. private TestModuleB1(IModuleContext moduleContext, ApplicationManager applicationManager) { // test the injection diff --git a/src/WebExpress.WebCore.Test/TestModuleC1.cs b/src/WebExpress.WebCore.Test/TestModuleC1.cs index 26436aa..0947444 100644 --- a/src/WebExpress.WebCore.Test/TestModuleC1.cs +++ b/src/WebExpress.WebCore.Test/TestModuleC1.cs @@ -17,7 +17,7 @@ public sealed class TestModuleC1 : IModule /// Initialization of the module. /// /// The module context, for testing the injection. - /// The application manager, for testing the injection. + /// The application manager, for testing the injection. private TestModuleC1(IModuleContext moduleContext, ApplicationManager applicationManager) { // test the injection diff --git a/src/WebExpress.WebCore.Test/TestModuleC2.cs b/src/WebExpress.WebCore.Test/TestModuleC2.cs index 80ba2ec..ae4a9cd 100644 --- a/src/WebExpress.WebCore.Test/TestModuleC2.cs +++ b/src/WebExpress.WebCore.Test/TestModuleC2.cs @@ -14,7 +14,7 @@ public sealed class TestModuleC2 : IModule /// Initialization of the module. ///
/// The module context, for testing the injection. - /// The application manager, for testing the injection. + /// The application manager, for testing the injection. private TestModuleC2(IModuleContext moduleContext, ApplicationManager applicationManager) { // test the injection diff --git a/src/WebExpress.WebCore.Test/TestPageA1Z.cs b/src/WebExpress.WebCore.Test/TestPageA1Z.cs index e54c6fb..a70603b 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestPageA1Z.cs @@ -41,7 +41,7 @@ public override void Process(IRenderContext context) /// /// Release of unmanaged resources reserved during use. /// - public void Dispose() + public override void Dispose() { } } diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 2587ef8..c56d901 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -134,6 +134,11 @@ sitemapmanager.merge.error=Die beiden Sitemaps '{0}' und '{1}' konnten nicht gem sessionmanager.initialization=Der Sessionmanager wurde initialisiert. eventmanager.initialization=Der Eventmanager wurde initialisiert. +eventmanager.applicationless=Der Eventhandler '{0}' besitzt keine Angaben zur Anwendung. +eventmanager.applicationrich=Der Eventhandler '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. +eventmanager.register=Der Eventhandler '{0}' wurde der Anwendung '{1}' zugewiesen und im Eventmanager registriert. +eventmanager.duplicate=Der Eventhandler '{0}' wurde bereits in der Anwendung '{1}' registriert. +eventmanager.eventless=Der Eventhandler '{0}' besitzt keine Angaben zu einem Event. jobmanager.initialization=Der Schedulemanager wurde initialisiert. jobmanager.register=Das Plugin '{0}' wurde im Schedulemanager registriert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 5d25a93..3edd518 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -134,6 +134,11 @@ sitemapmanager.merge.error=The two sitemaps '{0}' and '{1}' could not be merged. sessionmanager.initialization=The session manager has been initialized. eventmanager.initialization=The event manager has been initialized. +eventmanager.applicationless=The event handler '{0}' does not have any information about an application. +eventmanager.applicationrich=The event handler '{0}' has too many applications. The first application is used. +eventmanager.register=The event handler '{0}' has been registered in the application '{1}'. +eventmanager.duplicate=The event handler '{0}' has already been registered. Therefore, the application '{1}' is not used. +eventmanager.eventless=The event handler '{0}' does not have any information about the event. jobmanager.initialization=The schedule manager has been initialized. jobmanager.register=The plugin '{0}' is registered in the schedule manager manager. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 451227c..d00ffc4 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -249,6 +249,16 @@ public IApplicationContext GetApplication(Type application) return null; } + /// + /// Determines the application contexts for a given application id. + /// + /// The application type. + /// The context of the application or null. + public IApplicationContext GetApplication() + { + return GetApplication(typeof(T)); + } + /// /// Determines the application contexts for the given application ids. /// diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs index a035234..760940e 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -39,6 +39,13 @@ public interface IApplicationManager : IComponentManager /// The context of the application or null. IApplicationContext GetApplication(Type application); + /// + /// Determines the application contexts for a given application id. + /// + /// The application type. + /// The context of the application or null. + IApplicationContext GetApplication(); + /// /// Determines the application contexts for the given application ids. /// diff --git a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs index 6a3e658..991b257 100644 --- a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Application assignment attribute of the application ID. ///
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute + public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute, IEventAttribute { /// /// Initializes a new instance of the class. @@ -24,7 +24,7 @@ public ApplicationAttribute(string applicationId) /// /// The type of the application. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute where T : class, IApplication + public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute, IEventAttribute where T : class, IApplication { } diff --git a/src/WebExpress.WebCore/WebAttribute/EventAttribute.cs b/src/WebExpress.WebCore/WebAttribute/EventAttribute.cs new file mode 100644 index 0000000..0e7991a --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/EventAttribute.cs @@ -0,0 +1,21 @@ +using System; +using WebExpress.WebCore.WebEvent; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Specifying a event. + /// + /// The type of the event. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class EventAttribute : Attribute, IEventAttribute where T : class, IEvent + { + /// + /// Initializes a new instance of the class. + /// + public EventAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IEventAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IEventAttribute.cs new file mode 100644 index 0000000..451ca33 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IEventAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a event assignment attribute. + /// + public interface IEventAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index e270982..4a9c5f4 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -138,5 +138,49 @@ public static T CreateInstance(Type componentType, C context, IComponentHu return Activator.CreateInstance(componentType) as T; } + + /// + /// Creates an instance of the specified component type with the provided context and component hub and advanced parameters. + /// + /// The type of the component, which must implement . + /// The type of the context, which must implement . + /// The type of the component to create. + /// The context to pass to the component's constructor. + /// The component hub to use for dependency injection. + /// Additional parameters to pass to the component's constructor. + /// An instance of the specified component type. + public static IComponent CreateInstance(Type componentType, C context, IComponentHub componentHub, params object[] advancedParameters) where C : IContext + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(C) ? context : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is IComponent component) + { + return component; + } + } + } + + return Activator.CreateInstance(componentType) as IComponent; + } } } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 546f943..2a64088 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -36,6 +36,7 @@ public class ComponentHub : IComponentHub private readonly SitemapManager _sitemapManager; private readonly StatusPageManager _statusPageManager; private readonly SessionManager _sessionManager; + private readonly EventManager _eventManager; /// /// An event that fires when an component is added. @@ -66,11 +67,11 @@ public class ComponentHub : IComponentHub _resourceManager, _pageManager, _restApiManager, - EventManager, + _eventManager, JobManager, _statusPageManager, _internationalizationManager, - SessionManager, + _sessionManager, TaskManager }.Concat(_dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); @@ -108,7 +109,7 @@ public class ComponentHub : IComponentHub /// Returns the event manager. /// /// The instance of the event manager. - public EventManager EventManager { get; private set; } + public IEventManager EventManager => _eventManager; /// /// Returns the job manager. @@ -184,7 +185,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _pageManager = CreateInstance(typeof(PageManager)) as PageManager; _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; _statusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; - EventManager = CreateInstance(typeof(EventManager)) as EventManager; + _eventManager = CreateInstance(typeof(EventManager)) as EventManager; JobManager = CreateInstance(typeof(JobManager)) as JobManager; _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; diff --git a/src/WebExpress.WebCore/WebComponent/IComponent.cs b/src/WebExpress.WebCore/WebComponent/IComponent.cs index 7954ea8..9fd421c 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponent.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponent.cs @@ -1,9 +1,11 @@ -namespace WebExpress.WebCore.WebComponent +using System; + +namespace WebExpress.WebCore.WebComponent { /// /// Interface of a component. /// - public interface IComponent + public interface IComponent : IDisposable { } diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 691d148..0f11ce0 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -76,7 +76,7 @@ public interface IComponentHub : IComponentManager /// Returns the event manager. /// /// The instance of the event manager. - EventManager EventManager { get; } + IEventManager EventManager { get; } /// /// Returns the job manager. diff --git a/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs new file mode 100644 index 0000000..73a0468 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs @@ -0,0 +1,31 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebEvent +{ + /// + /// Represents the context of an event. + /// + public class EventHandlerContext : IEventHandlerContext + { + /// + /// Returns the event id. + /// + public string EventId { get; internal set; } + + /// + /// Returns the event handler id. + /// + public string EventHandlerId { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + } +} diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index e665d10..70ee8ba 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -1,9 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebEvent.Model; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebEvent @@ -11,41 +13,50 @@ namespace WebExpress.WebCore.WebEvent /// /// The event manager. /// - public sealed class EventManager : IComponentManagerPlugin, ISystemComponent + public sealed class EventManager : IEventManager, IComponentManagerPlugin, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly EventDictionary _dictionary = []; + /// + /// An event that fires when an event handler is added. + /// + public event EventHandler AddEventHandler; + + /// + /// An event that fires when an event handler is removed. + /// + public event EventHandler RemoveEventHandler; + + /// + /// Returns the collection of events. + /// + public IEnumerable EventHandlers => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.EventHandlerContext); + /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - internal EventManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + internal EventManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentHub; - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - _componentManager.ModuleManager.AddModule += (sender, moduleContext) => - { - AssignToModule(moduleContext); - }; - - _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => - { - DetachFromModule(moduleContext); - }; - _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -57,6 +68,47 @@ internal EventManager(IComponentHub componentManager, IHttpServerContext httpSer ); } + /// + /// Returns the event handler contexts. + /// + /// The type of event. + /// The application context. + /// An IEnumerable of event handler contexts. + public IEnumerable GetEventHandlers(IApplicationContext applicationContext) where T : IEvent + { + return _dictionary.GetEventHandlerItems(applicationContext) + .Select(x => x.EventHandlerContext); + } + + /// + /// Returns the event handler contexts. + /// + /// The application context. + /// The type of event. + /// An IEnumerable of event handler contexts. + public IEnumerable GetEventHandlers(IApplicationContext applicationContext, Type eventType) + { + return _dictionary.GetEventHandlerItems(applicationContext, eventType) + .Select(x => x.EventHandlerContext); + } + + /// + /// Raises the specified event. + /// + /// The type of event. + /// The application context. + /// The sender object. + /// The event argument. + public void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) where T : IEvent + { + var eventHandlers = _dictionary.GetEventHandlerItems(applicationContext); + + foreach (var eventHandler in eventHandlers) + { + eventHandler?.Process(sender, argument); + } + } + /// /// Discovers and registers event handlers from the specified plugin. /// @@ -70,10 +122,105 @@ public void Register(IPluginContext pluginContext) x => x.IsClass == true && x.IsSealed && x.IsPublic && - x.GetInterface(typeof(IEventHandler).Name) != null + ( + x.GetInterface(typeof(IEventHandler).Name) != null || + x.GetInterface(typeof(IEventHandler<>).Name) != null + ) )) { + var id = eventHandlerType.FullName?.ToLower(); + var applicationIds = new List(); + var eventType = default(Type); + + foreach (var customAttribute in eventHandlerType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEventAttribute)))) + { + if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) + { + applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + } + else if (customAttribute.AttributeType.Name == typeof(EventAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(EventAttribute<>).Namespace) + { + eventType = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + } + } + + if (!applicationIds.Any()) + { + // no application specified + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:eventmanager.applicationless", id) + ); + + break; + } + + if (applicationIds.Count() > 1) + { + // too many specified applications + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:eventmanager.applicationrich", id) + ); + } + + // assign the module to existing applications. + var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); + if (eventType != default) + { + var eventHandlerContext = new EventHandlerContext() + { + EventId = eventType.FullName.ToLower(), + EventHandlerId = id, + PluginContext = pluginContext, + ApplicationContext = applicationContext + }; + + if (_dictionary.AddEventItem + ( + pluginContext, + applicationContext, + new EventItem(_componentHub, pluginContext, applicationContext, eventHandlerContext, eventHandlerType, eventType) + )) + { + OnAddEventHandler(eventHandlerContext); + + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:eventmanager.register", + id, + applicationContext.ApplicationId + ) + ); + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:eventmanager.duplicate", + id, + applicationContext.ApplicationId + ) + ); + } + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:eventmanager.eventless", + id + ) + ); + } } } @@ -90,53 +237,41 @@ public void Register(IEnumerable pluginContexts) } /// - /// Assign existing event to the module. + /// Removes all jobs associated with the specified plugin context. /// - /// The context of the module. - private void AssignToModule(IModuleContext moduleContext) + /// The context of the plugin that contains the event to remove. + public void Remove(IPluginContext pluginContext) { - //foreach (var scheduleItem in _dictionary.Values.SelectMany(x => x)) - //{ - // if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) - // { - // scheduleItem.AddModule(moduleContext); - // } - //} + // the plugin has not been registered in the manager + if (_dictionary.TryGetValue(pluginContext, out var value)) + { + foreach (var eventHandlerItem in value + .SelectMany(x => x.Value) + .SelectMany(x => x.Value)) + { + eventHandlerItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + } } /// - /// Remove an existing modules to the event. + /// Raises the AddEventHandler event. /// - /// The context of the module. - private void DetachFromModule(IModuleContext moduleContext) + /// The event handler context. + private void OnAddEventHandler(IEventHandlerContext eventHandlerContext) { - //foreach (var scheduleItem in _dictionary.Values.SelectMany(x => x)) - //{ - // if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) - // { - // scheduleItem.DetachModule(moduleContext); - // } - //} + AddEventHandler?.Invoke(this, eventHandlerContext); } /// - /// Removes all jobs associated with the specified plugin context. + /// Raises the RemoveEventHandler event. /// - /// The context of the plugin that contains the event to remove. - public void Remove(IPluginContext pluginContext) + /// The event handler context. + private void OnRemoveEventHandler(IEventHandlerContext eventHandlerContext) { - //// the plugin has not been registered in the manager - //if (!_dictionary.ContainsKey(pluginContext)) - //{ - // return; - //} - - //foreach (var scheduleItem in _dictionary[pluginContext]) - //{ - // scheduleItem.Dispose(); - //} - - //_dictionary.Remove(pluginContext); + RemoveEventHandler?.Invoke(this, eventHandlerContext); } /// diff --git a/src/WebExpress.WebCore/WebEvent/IEvent.cs b/src/WebExpress.WebCore/WebEvent/IEvent.cs new file mode 100644 index 0000000..26c48e4 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/IEvent.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebEvent +{ + /// + /// Represents an event. + /// + public interface IEvent + { + } +} diff --git a/src/WebExpress.WebCore/WebEvent/IEventArgument.cs b/src/WebExpress.WebCore/WebEvent/IEventArgument.cs new file mode 100644 index 0000000..bebc6a5 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/IEventArgument.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebEvent +{ + /// + /// Represents an argument for an event. + /// + public interface IEventArgument + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebEvent/IEventContext.cs b/src/WebExpress.WebCore/WebEvent/IEventContext.cs deleted file mode 100644 index 72f9f67..0000000 --- a/src/WebExpress.WebCore/WebEvent/IEventContext.cs +++ /dev/null @@ -1,18 +0,0 @@ -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebEvent -{ - public interface IEventContext - { - /// - /// Returns the associated plugin context. - /// - IPluginContext PluginContext { get; } - - /// - /// Returns the corresponding module context. - /// - IModuleContext ModuleContext { get; } - } -} diff --git a/src/WebExpress.WebCore/WebEvent/IEventHandler.cs b/src/WebExpress.WebCore/WebEvent/IEventHandler.cs index 8dd298f..11b51ad 100644 --- a/src/WebExpress.WebCore/WebEvent/IEventHandler.cs +++ b/src/WebExpress.WebCore/WebEvent/IEventHandler.cs @@ -1,6 +1,30 @@ -namespace WebExpress.WebCore.WebEvent +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebEvent { - public interface IEventHandler + /// + /// Represents an event handler. + /// + public interface IEventHandler : IComponent { + /// + /// Process the event. + /// + /// The object that triggered the event. + /// The argument for the event. + void Process(object sender, IEventArgument eventArgument); + } + + /// + /// Represents an event handler. + /// + public interface IEventHandler : IComponent where T : class, IEventArgument + { + /// + /// Process the event. + /// + /// The object that triggered the event. + /// The argument for the event. + void Process(object sender, T eventArgument); } } diff --git a/src/WebExpress.WebCore/WebEvent/IEventHandlerContext.cs b/src/WebExpress.WebCore/WebEvent/IEventHandlerContext.cs new file mode 100644 index 0000000..e5f4b96 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/IEventHandlerContext.cs @@ -0,0 +1,32 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebEvent +{ + /// + /// Represents the context of an event. + /// + public interface IEventHandlerContext : IContext + { + /// + /// Returns the event id. + /// + string EventId { get; } + + /// + /// Returns the event handler id. + /// + string EventHandlerId { get; } + + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + } +} diff --git a/src/WebExpress.WebCore/WebEvent/IEventManager.cs b/src/WebExpress.WebCore/WebEvent/IEventManager.cs new file mode 100644 index 0000000..70d4396 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/IEventManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebEvent +{ + /// + /// Represents the interface for managing web events. + /// + public interface IEventManager : IComponentManager + { + /// + /// Returns the collection of events. + /// + IEnumerable EventHandlers { get; } + + /// + /// Returns the event handler contexts. + /// + /// The type of event. + /// The application context. + /// An IEnumerable of event handler contexts. + IEnumerable GetEventHandlers(IApplicationContext applicationContext) where T : IEvent; + + /// + /// Returns the event handler contexts. + /// + /// The application context. + /// The type of event. + /// An IEnumerable of event handler contexts. + IEnumerable GetEventHandlers(IApplicationContext applicationContext, Type eventType); + + /// + /// Raises the specified event. + /// + /// The type of event. + /// The application context. + /// The sender object. + /// The event argument. + void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) where T : IEvent; + } +} diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs index d488345..d012068 100644 --- a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs @@ -1,14 +1,152 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebEvent; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebEvent.Model { /// - /// key = plugin context - /// value = ressource items + /// Represents a dictionary that provides a mapping from plugin contexts to dictionaries of application contexts and event handler. /// - internal class EventDictionary : Dictionary> + internal class EventDictionary : Dictionary>>> { + /// + /// Adds a event item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The event item. + /// True if the event handler item was added successfully, false if an element with the same status code already exists. + public bool AddEventItem(IPluginContext pluginContext, IApplicationContext applicationContext, EventItem eventItem) + { + var type = eventItem.EventClass; + + if (!typeof(IEvent).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = []; + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = []; + } + + var eventDict = appContextDict[applicationContext]; + + if (!eventDict.ContainsKey(type)) + { + eventDict[type] = []; + } + + var eventList = eventDict[type]; + + if (eventList.Where(x => x.EventClass == type).Any()) + { + return false; // item with the same event handler already exists + } + + eventList.Add(eventItem); + + return true; + } + + /// + /// Removes a event from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemoveEventHandler(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IEvent + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var eventDict = appContextDict[applicationContext]; + + if (eventDict.ContainsKey(type)) + { + eventDict.Remove(type); + + if (eventDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the event handler from the dictionary. + /// + /// The type of event. + /// The application context. + /// An IEnumerable of event items + public IEnumerable GetEventHandlerItems(IApplicationContext applicationContext) where T : IEvent + { + return GetEventHandlerItems(applicationContext, typeof(T)); + } + + /// + /// Returns the event handler from the dictionary. + /// + /// The application context. + /// The type of event. + /// An IEnumerable of event items + public IEnumerable GetEventHandlerItems(IApplicationContext applicationContext, Type eventType) + { + if (!typeof(IEvent).IsAssignableFrom(eventType)) + { + return []; + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var eventDict = appContextDict[applicationContext]; + + if (eventDict.ContainsKey(eventType)) + { + return eventDict[eventType]; + } + } + } + + return []; + } + + /// + /// Returns all event handler contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of event handler contexts. + public IEnumerable GetEventHandlers(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .SelectMany(x => x) + .Select(x => x.EventHandlerContext); + } } } diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs new file mode 100644 index 0000000..cbae668 --- /dev/null +++ b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebEvent.Model +{ + /// + /// Represents an event item. + /// This class is intended for internal use only. + /// + internal class EventItem : IDisposable + { + private readonly IComponentHub _componentHub; + private readonly IDisposable _instance; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; private set; } + + /// + /// Returns or sets the event handler context. + /// + public IEventHandlerContext EventHandlerContext { get; private set; } + + /// + /// Returns or sets the event class. + /// + public Type EventClass { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The associated component hub. + /// The associated plugin context. + /// The corresponding application context. + /// The event handler context. + /// The event handler type. + /// The event class. + public EventItem(IComponentHub componentHub, IPluginContext pluginContext, IApplicationContext applicationContext, IEventHandlerContext eventHandlerContext, Type eventHandlerType, Type eventClass) + { + _componentHub = componentHub; + PluginContext = pluginContext; + ApplicationContext = applicationContext; + EventHandlerContext = eventHandlerContext; + EventClass = eventClass; + + if (typeof(IEventHandler).IsAssignableFrom(eventHandlerType)) + { + _instance = ComponentActivator.CreateInstance + ( + eventHandlerType, + eventHandlerContext, + _componentHub, + pluginContext, + applicationContext + ); + + return; + } + + _instance = ComponentActivator.CreateInstance + ( + eventHandlerType, + eventHandlerContext, + _componentHub, + pluginContext, + applicationContext + ); + } + + /// + /// Process the event. + /// + /// The object that triggered the event. + /// The argument for the event. + public void Process(object sender, IEventArgument eventArgument) + { + if (_instance is IEventHandler handler) + { + handler.Process(sender, eventArgument); + + return; + } + + var handlerType = _instance.GetType() + .GetInterfaces() + .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEventHandler<>)); + + if (handlerType != null) + { + var genericArgument = handlerType.GetGenericArguments().First(); + var method = handlerType.GetMethod("Process"); + method.Invoke(_instance, [sender, eventArgument]); + } + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + _instance?.Dispose(); + } + + /// + /// Convert the resource element to a string. + /// + /// The event element in its string representation. + public override string ToString() + { + return $"Event '{EventClass.FullName.ToLower()}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs index 846c6b8..c33a97c 100644 --- a/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs @@ -65,5 +65,14 @@ public class PackageItem internal PackageItem() { } + + /// + /// Convert the package element to a string. + /// + /// The package element in its string representation. + public override string ToString() + { + return $"Package '{Id}'"; + } } } diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index 82ee5ac..38ec06e 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -39,5 +39,10 @@ public void Redirecting(string uri) /// /// The context for rendering the page. public abstract void Process(IRenderContext context); + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public abstract void Dispose(); } } diff --git a/src/WebExpress.WebCore/WebResource/Resource.cs b/src/WebExpress.WebCore/WebResource/Resource.cs index 863f95a..9977312 100644 --- a/src/WebExpress.WebCore/WebResource/Resource.cs +++ b/src/WebExpress.WebCore/WebResource/Resource.cs @@ -1,55 +1,24 @@ -using System.Globalization; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebResource { + /// + /// Represents a resource in the web application. + /// public abstract class Resource : IResource { - /// - /// Returns the context of the application. - /// - public IApplicationContext ApplicationContext => ResourceContext?.ModuleContext?.ApplicationContext; - - /// - /// Returns the context of the module. - /// - public IModuleContext ModuleContext => ResourceContext?.ModuleContext; - /// /// Returns the resource context where the resource exists. /// public IResourceContext ResourceContext { get; private set; } - /// - /// Provides the culture. - /// - public CultureInfo Culture { get; set; } - /// /// Initializes a new instance of the class. - /// - public Resource() - { - } - - /// - /// Initialization - /// /// The context of the resource. - public virtual void Initialization(IResourceContext resourceContext) - { - ResourceContext = resourceContext; - } - - /// - /// Preprocessing of the resource. /// - /// The request. - public virtual void PreProcess(Request request) + public Resource(IResourceContext resourceContext) { - return; + ResourceContext = resourceContext; } /// @@ -60,14 +29,11 @@ public virtual void PreProcess(Request request) public abstract Response Process(Request request); /// - /// Post-processing of the resource. + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. /// - /// The request. - /// The response. - /// The response. - public virtual Response PostProcess(Request request, Response response) + public virtual void Dispose() { - return response; + } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index b686657..3b5d7da 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -27,6 +27,7 @@ public class ResourceAsset : ResourceBinary /// /// The resource context. public ResourceAsset(IResourceContext resourceContext) + : base(resourceContext) { Gard = new object(); AssetDirectory = ResourceContext.PluginContext.Assembly.GetName().Name; @@ -147,5 +148,13 @@ private static byte[] GetData(string file, Assembly assembly, IEnumerable + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + + } } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebResource/ResourceBinary.cs b/src/WebExpress.WebCore/WebResource/ResourceBinary.cs index faa9b2a..396966a 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceBinary.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceBinary.cs @@ -15,7 +15,9 @@ public abstract class ResourceBinary : Resource /// /// Initializes a new instance of the class. /// - public ResourceBinary() + /// The resource context. + public ResourceBinary(IResourceContext resourceContext) + : base(resourceContext) { } diff --git a/src/WebExpress.WebCore/WebResource/ResourceFile.cs b/src/WebExpress.WebCore/WebResource/ResourceFile.cs index 371f23d..b24ce89 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceFile.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceFile.cs @@ -21,20 +21,13 @@ public class ResourceFile : ResourceBinary /// /// Initializes a new instance of the class. /// - public ResourceFile() + /// The resource context. + public ResourceFile(IResourceContext resourceContext) + : base(resourceContext) { Gard = new object(); } - /// - /// Initialization - /// - /// The context. - public override void Initialization(IResourceContext context) - { - base.Initialization(context); - } - /// /// Processing of the resource. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceRest.cs b/src/WebExpress.WebCore/WebResource/ResourceRest.cs deleted file mode 100644 index 8749b66..0000000 --- a/src/WebExpress.WebCore/WebResource/ResourceRest.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Linq; -using System.Text.Json; -using WebExpress.WebCore.WebMessage; - -namespace WebExpress.WebCore.WebResource -{ - public abstract class ResourceRest : Resource - { - /// - /// Initializes a new instance of the class. - /// - public ResourceRest() - { - - } - - /// - /// Initialization - /// - /// The context. - public override void Initialization(IResourceContext context) - { - base.Initialization(context); - } - - /// - /// Processing of the resource that was called via the get request. - /// - /// The request. - /// An enumeration of which json serializer can be serialized. - public virtual object GetData(Request request) - { - return null; - } - - /// - /// Processing of the resource that was called via the delete request. - /// - /// The id to delete. - /// The request. - /// The result of the deletion. - public virtual bool DeleteData(string id, Request request) - { - return false; - } - - /// - /// Processing of the resource. - /// - /// The request. - /// The response. - public override Response Process(Request request) - { - var options = new JsonSerializerOptions - { - WriteIndented = true - }; - - var response = new ResponseOK(); - var content = string.Empty; - - switch (request.Method) - { - case RequestMethod.GET: - { - content = JsonSerializer.Serialize(GetData(request), options); - - break; - } - case RequestMethod.DELETE: - { - content = JsonSerializer.Serialize(DeleteData(request.Uri.PathSegments.Last()?.ToString(), request), options); - - break; - } - }; - - response.Header.ContentLength = content.Length; - response.Content = content; - - return response; - } - } -} diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApi.cs b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs index 45dda27..eec24c4 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApi.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApi.cs @@ -56,5 +56,13 @@ public virtual void DeleteData(Request request) { } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public virtual void Dispose() + { + + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs index f42a1f9..5fbf3a1 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs @@ -119,10 +119,10 @@ public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, } /// - /// Returns all StatusPageContexts for a given PluginContext. + /// Returns all StatusPageContexts for a given plugin context. /// - /// The PluginContext. - /// An IEnumerable of StatusPageContexts. + /// The plugin context. + /// An IEnumerable of status page contexts. public IEnumerable GetStatusPageContexts(IPluginContext pluginContext) { return this.Where(entry => entry.Key == pluginContext) diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs index 452a082..4808081 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs @@ -45,5 +45,14 @@ internal class StatusPageItem public void Dispose() { } + + /// + /// Convert the status page element to a string. + /// + /// The status page element in its string representation. + public override string ToString() + { + return $"StatusPage '{StatusPageId}'"; + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 86822ae..697036b 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -393,5 +393,13 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ); } } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + + } } } From 587a6ad8c457c7ce4f07e714b1c791b0efe39653 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 20 Oct 2024 16:47:53 +0200 Subject: [PATCH 030/162] restructured job manager and added tests - refactored job manager for improved readability and maintainability. - added unit tests to cover all major functionalities. --- .../Manager/UnitTestJobManager.cs | 98 +++++++ src/WebExpress.WebCore.Test/TestJobA1.cs | 45 ++++ .../Internationalization/de | 11 +- .../Internationalization/en | 9 +- .../WebComponent/ComponentHub.cs | 11 +- .../WebComponent/IComponentHub.cs | 2 +- src/WebExpress.WebCore/WebJob/IJob.cs | 12 +- src/WebExpress.WebCore/WebJob/IJobContext.cs | 12 +- src/WebExpress.WebCore/WebJob/IJobManager.cs | 16 ++ src/WebExpress.WebCore/WebJob/Job.cs | 27 +- src/WebExpress.WebCore/WebJob/JobContext.cs | 28 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 248 ++++++++---------- .../WebJob/Model/ScheduleDictionary.cs | 148 ++++++++++- .../WebJob/Model/ScheduleIDynamicItem.cs | 25 -- .../WebJob/Model/ScheduleItem.cs | 90 +++++++ .../WebJob/Model/ScheduleStaticItem.cs | 181 ------------- .../WebJob/Model/ScheduleStaticItemValue.cs | 25 -- 17 files changed, 546 insertions(+), 442 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestJobA1.cs create mode 100644 src/WebExpress.WebCore/WebJob/IJobManager.cs delete mode 100644 src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs create mode 100644 src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs delete mode 100644 src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs delete mode 100644 src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs new file mode 100644 index 0000000..e9cdd93 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs @@ -0,0 +1,98 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebJob; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the job manager. + /// + [Collection("NonParallelTests")] + public class UnitTestJobManager + { + /// + /// Test the register function of the job manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(1, componentHub.JobManager.Jobs.Count()); + } + + /// + /// Test the remove function of the job manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var jobManager = componentHub.JobManager as JobManager; + + // test execution + jobManager.Remove(plugin); + + Assert.Empty(componentHub.JobManager.Jobs); + } + + /// + /// Tests whether the job manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); + } + + /// + /// Tests whether the job context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + foreach (var application in componentHub.ResourceManager.Resources) + { + Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Resource context {application.GetType().Name} does not implement IContext."); + } + } + + /// + /// Test the id property of the job. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webexpress.webcore.test.testresourcea1x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "webexpress.webcore.test.testresourcea1y")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webexpress.webcore.test.testresourcea2x")] + [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] + [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestPageA1X), null)] + + public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); + var resource = componentHub.ResourceManager.GetResorce(module, resourceType); + + // test execution + Assert.Equal(id, resource?.EndpointId); + } + + + + + } +} diff --git a/src/WebExpress.WebCore.Test/TestJobA1.cs b/src/WebExpress.WebCore.Test/TestJobA1.cs new file mode 100644 index 0000000..a065a58 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestJobA1.cs @@ -0,0 +1,45 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebJob; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy job for testing purposes. + /// + [Application()] + [Name("module.namea1")] + [Description("module.descriptiona1")] + [Icon("/assets/img/Logo.png")] + [ContextPath("/mca")] + [AssetPath("/maa")] + [DataPath("/mda")] + public sealed class TestJobA1 : IJob + { + /// + /// Initialization of the job. + /// + /// The module context, for testing the injection. + private TestJobA1(IJobContext jobContext) + { + // test the injection + if (jobContext == null) + { + throw new ArgumentNullException(nameof(jobContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Called when the jobs starts working. The call is concurrent. + /// + public void Process() + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index c56d901..645bc58 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -140,11 +140,12 @@ eventmanager.register=Der Eventhandler '{0}' wurde der Anwendung '{1}' zugewiese eventmanager.duplicate=Der Eventhandler '{0}' wurde bereits in der Anwendung '{1}' registriert. eventmanager.eventless=Der Eventhandler '{0}' besitzt keine Angaben zu einem Event. -jobmanager.initialization=Der Schedulemanager wurde initialisiert. -jobmanager.register=Das Plugin '{0}' wurde im Schedulemanager registriert. -jobmanager.job.register=Der Job '{1}' wurden dem Modul '{0}' zugewiesen. -jobmanager.moduleless=Der Job '{0}' besitzt keine Angaben zum Modul. -jobmanager.wrongmodule=Der Job '{1}' ist kein Teil des Moduls {0}. +jobmanager.initialization=Der Jobmanager wurde initialisiert. +jobmanager.applicationless=Der Job '{0}' besitzt keine Angaben zur Anwendung. +jobmanager.applicationrich=Der Job '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. +jobmanager.register=Der Job '{0}' wurde der Anwendung '{1}' zugewiesen und im Jobmanager registriert. +jobmanager.duplicate=Der Job '{0}' wurde bereits in der Anwendung '{1}' registriert. +jobmanager.jobless=Der Job '{0}' besitzt keine Angaben zu einem Job. jobmanager.job=Job: '{0}' für Modul '{1}' jobmanager.job.process=Der Job '{0}' wird ausgeführt. jobmanager.cron.parseerror=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' kann nicht verarbeitet werden. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 3edd518..2860a9b 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -141,10 +141,11 @@ eventmanager.duplicate=The event handler '{0}' has already been registered. Ther eventmanager.eventless=The event handler '{0}' does not have any information about the event. jobmanager.initialization=The schedule manager has been initialized. -jobmanager.register=The plugin '{0}' is registered in the schedule manager manager. -jobmanager.job.register=The job '{1}' have been assigned to the module '{0}'. -jobmanager.moduleless=The job '{0}' does not have any information about the module. -jobmanager.wrongmodule=The '{1}' job is not part of the module {0}. +eventmanager.applicationless=The job '{0}' does not have any information about an application. +eventmanager.applicationrich=The job '{0}' has too many applications. The first application is used. +eventmanager.register=The job '{0}' has been registered in the application '{1}'. +eventmanager.duplicate=The job '{0}' has already been registered. Therefore, the application '{1}' is not used. +eventmanager.jobless=The job '{0}' does not have any information about the job. jobmanager.job=Job: '{0}' for module '{1}' jobmanager.job.process=The job '{0}' is executed. jobmanager.cron.parseerror=Syntax error in the timing of a job. The value '{0}' cannot be processed. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 2a64088..87e2000 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -37,6 +37,7 @@ public class ComponentHub : IComponentHub private readonly StatusPageManager _statusPageManager; private readonly SessionManager _sessionManager; private readonly EventManager _eventManager; + private readonly JobManager _jobManager; /// /// An event that fires when an component is added. @@ -68,7 +69,7 @@ public class ComponentHub : IComponentHub _pageManager, _restApiManager, _eventManager, - JobManager, + _jobManager, _statusPageManager, _internationalizationManager, _sessionManager, @@ -115,7 +116,7 @@ public class ComponentHub : IComponentHub /// Returns the job manager. /// /// The instance of the job manager. - public JobManager JobManager { get; private set; } + public IJobManager JobManager => _jobManager; /// /// Returns the resource manager. @@ -186,7 +187,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; _statusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; _eventManager = CreateInstance(typeof(EventManager)) as EventManager; - JobManager = CreateInstance(typeof(JobManager)) as JobManager; + _jobManager = CreateInstance(typeof(JobManager)) as JobManager; _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; @@ -377,7 +378,7 @@ internal void Execute() ); PackageManager.Execute(); - JobManager.Execute(); + _jobManager.Execute(); } /// @@ -482,7 +483,7 @@ internal void LogStatus() _moduleManager.PrepareForLog(pluginContext, output, 4); _resourceManager.PrepareForLog(pluginContext, output, 4); _statusPageManager.PrepareForLog(pluginContext, output, 4); - JobManager.PrepareForLog(pluginContext, output, 4); + _jobManager.PrepareForLog(pluginContext, output, 4); } //foreach (var item in Dictionary) diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 0f11ce0..8e1b04a 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -82,7 +82,7 @@ public interface IComponentHub : IComponentManager /// Returns the job manager. /// /// The instance of the job manager. - JobManager JobManager { get; } + IJobManager JobManager { get; } /// /// Returns the resource manager. diff --git a/src/WebExpress.WebCore/WebJob/IJob.cs b/src/WebExpress.WebCore/WebJob/IJob.cs index 91f82fc..f84bd0e 100644 --- a/src/WebExpress.WebCore/WebJob/IJob.cs +++ b/src/WebExpress.WebCore/WebJob/IJob.cs @@ -1,20 +1,14 @@ -using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebJob { /// /// A task that can be performed cyclically. /// - public interface IJob : II18N + public interface IJob : IComponent { /// - /// Initialization - /// - /// The context in which the job is executed. - public void Initialization(IJobContext context); - - /// - /// Processing of the resource. + /// Processing of the job. /// public void Process(); diff --git a/src/WebExpress.WebCore/WebJob/IJobContext.cs b/src/WebExpress.WebCore/WebJob/IJobContext.cs index f99bcb9..34adba6 100644 --- a/src/WebExpress.WebCore/WebJob/IJobContext.cs +++ b/src/WebExpress.WebCore/WebJob/IJobContext.cs @@ -1,9 +1,13 @@ -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebJob { - public interface IJobContext + /// + /// Represents the context of a job. + /// + public interface IJobContext : IContext { /// /// Returns the associated plugin context. @@ -11,9 +15,9 @@ public interface IJobContext IPluginContext PluginContext { get; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - IModuleContext ModuleContext { get; } + IApplicationContext ApplicationContext { get; } /// /// Returns the job id. diff --git a/src/WebExpress.WebCore/WebJob/IJobManager.cs b/src/WebExpress.WebCore/WebJob/IJobManager.cs new file mode 100644 index 0000000..8c65765 --- /dev/null +++ b/src/WebExpress.WebCore/WebJob/IJobManager.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebJob +{ + /// + /// Processing of cyclic jobs. + /// + public interface IJobManager : IComponentManager + { + /// + /// Returns all jobs contextes. + /// + IEnumerable Jobs { get; } + } +} diff --git a/src/WebExpress.WebCore/WebJob/Job.cs b/src/WebExpress.WebCore/WebJob/Job.cs index b4e9a11..64eee59 100644 --- a/src/WebExpress.WebCore/WebJob/Job.cs +++ b/src/WebExpress.WebCore/WebJob/Job.cs @@ -1,7 +1,4 @@ - -using System.Globalization; - -namespace WebExpress.WebCore.WebJob +namespace WebExpress.WebCore.WebJob { /// /// A task that can be performed cyclically. @@ -9,30 +6,18 @@ namespace WebExpress.WebCore.WebJob public class Job : IJob { /// - /// Returns or sets the culture. - /// - public CultureInfo Culture { get; set; } - - /// - /// Returns the context. - /// - public IJobContext Context { get; private set; } - - /// - /// Initialization + /// Processing of the resource. /// - /// The context in which the job is executed. - public virtual void Initialization(IJobContext context) + public virtual void Process() { - Context = context; + } /// - /// Processing of the resource. + /// Release of unmanaged resources reserved during use. /// - public virtual void Process() + public virtual void Dispose() { - } } } diff --git a/src/WebExpress.WebCore/WebJob/JobContext.cs b/src/WebExpress.WebCore/WebJob/JobContext.cs index ac084db..08905c4 100644 --- a/src/WebExpress.WebCore/WebJob/JobContext.cs +++ b/src/WebExpress.WebCore/WebJob/JobContext.cs @@ -1,8 +1,11 @@ -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebJob { + /// + /// Represents the job context. + /// public class JobContext : IJobContext { /// @@ -11,9 +14,9 @@ public class JobContext : IJobContext public IPluginContext PluginContext { get; internal set; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - public IModuleContext ModuleContext { get; internal set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns the job id. @@ -24,24 +27,5 @@ public class JobContext : IJobContext /// Returns the cron-object. /// public Cron Cron { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - /// The module context. - internal JobContext(IModuleContext moduleContext) - { - PluginContext = moduleContext?.PluginContext; - ModuleContext = moduleContext; - } - - /// - /// Initializes a new instance of the class. - /// - /// The plugin context. - internal JobContext(IPluginContext pluginContext) - { - PluginContext = pluginContext; - } } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 1d52a8b..648f500 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -7,23 +7,42 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebJob.Model; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebJob { - /// - /// Processing of cyclic jobs. - /// - public sealed class JobManager : IComponentManagerPlugin, ISystemComponent, IExecutableElements + /// + /// This class manages the processing of cyclic jobs. It provides methods to register, remove, and execute jobs. + /// + public sealed class JobManager : IJobManager, IComponentManagerPlugin, ISystemComponent, IExecutableElements { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly ScheduleDictionary _staticScheduleDictionary = []; - private readonly List _dynamicScheduleList = []; + private readonly List _dynamicScheduleList = []; private readonly CancellationTokenSource _tokenSource = new(); private readonly Clock _clock = new(); + /// + /// An event that fires when an job is added. + /// + public event EventHandler AddJob; + + /// + /// An event that fires when an job is removed. + /// + public event EventHandler RemoveJob; + + /// + /// Returns all jobs contextes. + /// + public IEnumerable Jobs => _staticScheduleDictionary + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Select(x => x.JobContext) + .Union(_dynamicScheduleList.Select(x => x.JobContext)); + /// /// Initializes a new instance of the class. /// @@ -31,28 +50,18 @@ public sealed class JobManager : IComponentManagerPlugin, ISystemComponent, IExe /// The reference to the context of the host. internal JobManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentManager; - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; - _componentManager.ModuleManager.AddModule += (sender, moduleContext) => - { - AssignToModule(moduleContext); - }; - - _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => - { - DetachFromModule(moduleContext); - }; - _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -81,12 +90,12 @@ public void Register(IPluginContext pluginContext) )) { var id = job.FullName?.ToLower(); + var applicationIds = new List(); var minute = "*"; var hour = "*"; var day = "*"; var month = "*"; var weekday = "*"; - var moduleId = string.Empty; foreach (var customAttribute in job.CustomAttributes.Where(x => x.AttributeType == typeof(JobAttribute))) { @@ -98,68 +107,89 @@ public void Register(IPluginContext pluginContext) } foreach (var customAttribute in job.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IModuleAttribute)))) + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEventAttribute)))) { - if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) + if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) { - moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); } } - if (string.IsNullOrWhiteSpace(moduleId)) + if (!applicationIds.Any()) { - // no module specified + // no application specified _httpServerContext.Log.Warning ( - I18N.Translate - ( - "webexpress:jobmanager.moduleless", id - ) + I18N.Translate("webexpress:jobmanager.applicationless", id) ); + + break; } - // register the job - if (!_staticScheduleDictionary.ContainsKey(pluginContext)) + if (applicationIds.Count() > 1) { - _staticScheduleDictionary.Add(pluginContext, new List()); + // too many specified applications + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:jobmanager.applicationrich", id) + ); } - var dictItem = _staticScheduleDictionary[pluginContext]; + // assign the module to existing applications. + var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); - dictItem.Add(new ScheduleStaticItem() + if (job != default) { - Assembly = assembly, - JobId = id, - Type = job, - Cron = new Cron(pluginContext.Host, minute, hour, day, month, weekday), - moduleId = moduleId - }); - - _httpServerContext.Log.Debug - ( - I18N.Translate + var jobContext = new JobContext() + { + JobId = job.FullName.ToLower(), + PluginContext = pluginContext, + ApplicationContext = applicationContext, + Cron = new Cron(pluginContext.Host, minute, hour, day, month, weekday), + }; + + if (_staticScheduleDictionary.AddScheduleItem ( - "webexpress:jobmanager.job.register", moduleId, id - ) - ); + pluginContext, + applicationContext, + new ScheduleItem(_componentHub, pluginContext, applicationContext, jobContext, job) + )) + { + OnAddJob(jobContext); - // assign the job to existing modules. - foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) - { - if (moduleContext.PluginContext != pluginContext) + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:jobmanager.register", + id, + applicationContext.ApplicationId + ) + ); + } + else { - // job is not part of the module - _httpServerContext.Log.Warning + _httpServerContext.Log.Debug ( I18N.Translate ( - "webexpress:jobmanager.wrongmodule", - moduleContext.ModuleId, id + "webexpress:jobmanager.duplicate", + id, + applicationContext.ApplicationId ) ); } - - AssignToModule(moduleContext); + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:jobmanager.jobless", + id + ) + ); } } } @@ -185,98 +215,38 @@ public void Register(IEnumerable pluginContexts) public IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob { // create context - var jobContext = new JobContext(pluginContext) - { - JobId = typeof(T).FullName?.ToLower(), - Cron = cron - }; - - var jobInstance = Activator.CreateInstance(typeof(T)) as IJob; - jobInstance.Initialization(jobContext); - - var item = new ScheduleDynamicItem() - { - JobContext = jobContext, - Instance = jobInstance - }; - - _dynamicScheduleList.Append(item); - - return jobInstance; - } - - /// - /// Registers a job. - /// - /// The module context. - /// The cropn-object. - /// The job. - public IJob Register(IModuleContext moduleContext, Cron cron) where T : IJob - { - // create context - var jobContext = new JobContext(moduleContext) + var jobContext = new JobContext() { + PluginContext = pluginContext, JobId = typeof(T).FullName?.ToLower(), Cron = cron }; - var jobInstance = Activator.CreateInstance(typeof(T)) as IJob; - jobInstance.Initialization(jobContext); - - var item = new ScheduleDynamicItem() - { - JobContext = jobContext, - Instance = jobInstance - }; + var item = new ScheduleItem(_componentHub, pluginContext, null, jobContext, typeof(T)); _dynamicScheduleList.Append(item); - return jobInstance; - } + OnAddJob(jobContext); - /// - /// Assign existing job to the module. - /// - /// The context of the module. - private void AssignToModule(IModuleContext moduleContext) - { - foreach (var scheduleItem in _staticScheduleDictionary.Values.SelectMany(x => x)) - { - if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) - { - scheduleItem.AddModule(moduleContext); - } - } + return item.Instance; } /// - /// Remove an existing modules to the job. + /// Raises the AddJob event. /// - /// The context of the module. - private void DetachFromModule(IModuleContext moduleContext) + /// The job context. + private void OnAddJob(IJobContext jobContext) { - foreach (var scheduleItem in _staticScheduleDictionary.Values.SelectMany(x => x)) - { - if (scheduleItem.moduleId.Equals(moduleContext?.ModuleId)) - { - scheduleItem.DetachModule(moduleContext); - } - } + AddJob?.Invoke(this, jobContext); } /// - /// Retruns the schedule item for a given plugin. + /// Raises the RemoveJob event. /// - /// The context of the plugin. - /// An enumeration of the schedule item for the given plugin. - internal IEnumerable GetScheduleItems(IPluginContext pluginContext) + /// The job context. + private void OnRemoveJob(IJobContext jobContext) { - if (pluginContext == null || !_staticScheduleDictionary.ContainsKey(pluginContext)) - { - return Enumerable.Empty(); - } - - return _staticScheduleDictionary[pluginContext]; + RemoveJob?.Invoke(this, jobContext); } /// @@ -304,9 +274,11 @@ private void Update() { foreach (var clock in _clock.Synchronize()) { - foreach (var scheduleItemValue in _staticScheduleDictionary.Values - .SelectMany(x => x) - .SelectMany(x => x.Dictionary.Values)) + foreach (var scheduleItemValue in _staticScheduleDictionary + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Union(_dynamicScheduleList.Select(x => x))) { if (scheduleItemValue.JobContext.Cron.Matching(_clock)) { @@ -373,7 +345,9 @@ public void Remove(IPluginContext pluginContext) return; } - foreach (var scheduleItem in _staticScheduleDictionary[pluginContext]) + foreach (var scheduleItem in _staticScheduleDictionary[pluginContext].Values + .SelectMany(x => x.Values) + .SelectMany(x => x)) { scheduleItem.Dispose(); } @@ -398,7 +372,7 @@ public void Remove(IJob job) /// The shaft deep. public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { - foreach (var scheduleItem in GetScheduleItems(pluginContext)) + foreach (var scheduleItem in Jobs.Where(x => x.PluginContext == pluginContext)) { output.Add ( @@ -407,7 +381,7 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ( "webexpress:jobmanager.job", scheduleItem.JobId, - scheduleItem.ModuleContext + scheduleItem.ApplicationContext ) ); } diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs index d0dd095..f481d0e 100644 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs @@ -1,13 +1,155 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebJob.Model { /// + /// Represents a dictionary that provides a mapping from plugin contexts to dictionaries of application contexts and jobs. /// key = plugin context - /// value = ressource items + /// value = scheduler items /// - internal class ScheduleDictionary : Dictionary> + internal class ScheduleDictionary : Dictionary>>> { + /// + /// Adds a event item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The schedule item. + /// True if the schedule item was added successfully, false if an element with the same status code already exists. + public bool AddScheduleItem(IPluginContext pluginContext, IApplicationContext applicationContext, ScheduleItem scheduleItem) + { + var type = scheduleItem.JobClass; + + if (!typeof(IJob).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = []; + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = []; + } + + var scheduleDict = appContextDict[applicationContext]; + + if (!scheduleDict.ContainsKey(type)) + { + scheduleDict[type] = []; + } + + var scheduleList = scheduleDict[type]; + + if (scheduleList.Where(x => x.JobClass == type).Any()) + { + return false; // item with the same event handler already exists + } + + scheduleList.Add(scheduleItem); + + return true; + } + + /// + /// Removes a schedule item from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemoveScheduleItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IEvent + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var scheduleDict = appContextDict[applicationContext]; + + if (scheduleDict.ContainsKey(type)) + { + scheduleDict.Remove(type); + + if (scheduleDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the schedule items from the dictionary. + /// + /// The type of event. + /// The application context. + /// An IEnumerable of schedule items + public IEnumerable GetScheduleItems(IApplicationContext applicationContext) where T : IJob + { + return GetScheduleItems(applicationContext, typeof(T)); + } + + /// + /// Returns the schedule items from the dictionary. + /// + /// The application context. + /// The type of job. + /// An IEnumerable of event items + public IEnumerable GetScheduleItems(IApplicationContext applicationContext, Type jobType) + { + if (!typeof(IEvent).IsAssignableFrom(jobType)) + { + return []; + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var scheduleDict = appContextDict[applicationContext]; + + if (scheduleDict.ContainsKey(jobType)) + { + return scheduleDict[jobType]; + } + } + } + + return []; + } + + /// + /// Returns all job contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of job contexts. + public IEnumerable GetJobs(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .SelectMany(x => x) + .Select(x => x.JobContext); + } } } diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs deleted file mode 100644 index 13e5392..0000000 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleIDynamicItem.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; - -namespace WebExpress.WebCore.WebJob.Model -{ - /// - /// Represents an job entry in the dynamic job execution list. - /// - internal class ScheduleDynamicItem - { - /// - /// The context associated with the job. - /// - public IJobContext JobContext { get; set; } - - /// - /// Returns the job instance. - /// - public IJob Instance { get; internal set; } - - /// - /// Returns the cancel token or null if not already created. - /// - public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); - } -} diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs new file mode 100644 index 0000000..ce189f1 --- /dev/null +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebJob.Model +{ + /// + /// Represents an appointment entry in the appointment execution directory. + /// + internal class ScheduleItem : IDisposable + { + private readonly IComponentHub _componentHub; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// The context associated with the job. + /// + public IJobContext JobContext { get; internal set; } + + /// + /// Returns the job class. + /// + public Type JobClass { get; internal set; } + + /// + /// Returns the job instance. + /// + public IJob Instance { get; internal set; } + + /// + /// Returns the cancel token or null if not already created. + /// + public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); + + /// + /// Initializes a new instance of the class. + /// + /// The associated component hub. + /// The associated plugin context. + /// The corresponding application context. + /// The job context. + /// The job class. + public ScheduleItem(IComponentHub componentHub, IPluginContext pluginContext, IApplicationContext applicationContext, IJobContext jobContext, Type jobClass) + { + _componentHub = componentHub; + PluginContext = pluginContext; + ApplicationContext = applicationContext; + JobContext = jobContext; + JobClass = jobClass; + + Instance = ComponentActivator.CreateInstance + ( + jobClass, + jobContext, + _componentHub, + pluginContext, + applicationContext + ); + + return; + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Convert the resource element to a string. + /// + /// The resource element in its string representation. + public override string ToString() + { + return "Job ${Id}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs deleted file mode 100644 index 56bf97a..0000000 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItem.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebJob.Model -{ - /// - /// Represents an appointment entry in the appointment execution directory - /// - internal class ScheduleStaticItem - { - /// - /// The assembly that contains the module. - /// - public Assembly Assembly { get; internal set; } - - /// - /// Returns the associated plugin context. - /// - public IPluginContext PluginContext { get; internal set; } - - /// - /// Returns the corresponding module context. - /// - public IModuleContext ModuleContext { get; internal set; } - - /// - /// Returns the job id. - /// - public string JobId { get; internal set; } - - /// - /// Returns the cron object. - /// - public Cron Cron { get; internal set; } - - /// - /// Returns the log to write status messages to the console and to a log file. - /// - public Log Log { get; internal set; } - - /// - /// Returns the job class. - /// - public Type Type { get; internal set; } - - /// - /// Returns or sets the module id. - /// - public string moduleId { get; set; } - - /// - /// Returns the directory where the job instances are listed. - /// - public IDictionary Dictionary { get; } - = new Dictionary(); - - /// - /// An event that fires when an job is added. - /// - public event EventHandler AddJob; - - /// - /// An event that fires when an job is removed. - /// - public event EventHandler RemoveJob; - - /// - /// Adds an module assignment - /// - /// The context of the module. - public void AddModule(IModuleContext moduleContext) - { - // only if no instance has been created yet - if (Dictionary.ContainsKey(moduleContext)) - { - return; - } - - // Only for the right module - if (!moduleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - // create context - var jobContext = new JobContext(moduleContext) - { - JobId = JobId, - PluginContext = PluginContext, - Cron = Cron - }; - - var jobInstance = Activator.CreateInstance(Type) as IJob; - jobInstance.Initialization(jobContext); - - Dictionary.Add - ( - moduleContext, - new ScheduleStaticItemValue() - { - JobContext = jobContext, - Instance = jobInstance - } - ); - - OnAddJob(jobContext); - } - - /// - /// Remove an module assignment - /// - /// The context of the module. - public void DetachModule(IModuleContext moduleContext) - { - // not an assignment has been created yet - if (!Dictionary.ContainsKey(moduleContext)) - { - return; - } - - foreach (var scheduleItemValue in Dictionary.Values) - { - OnRemoveResource(scheduleItemValue.JobContext); - } - - Dictionary.Remove(moduleContext); - } - - /// - /// Raises the AddJob event. - /// - /// The job context. - private void OnAddJob(IJobContext jobContext) - { - AddJob?.Invoke(this, jobContext); - } - - /// - /// Raises the RemoveJob event. - /// - /// The job context. - private void OnRemoveResource(IJobContext jobContext) - { - RemoveJob?.Invoke(this, jobContext); - } - - /// - /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. - /// - public void Dispose() - { - foreach (var d in AddJob.GetInvocationList()) - { - AddJob -= (EventHandler)d; - } - - foreach (var d in RemoveJob.GetInvocationList()) - { - RemoveJob -= (EventHandler)d; - } - - foreach (var scheduleItemValue in Dictionary.Values) - { - scheduleItemValue.TokenSource.Cancel(); - } - } - - /// - /// Convert the resource element to a string. - /// - /// The resource element in its string representation. - public override string ToString() - { - return "Job ${Id}"; - } - } -} diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs deleted file mode 100644 index d9bb65d..0000000 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleStaticItemValue.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; - -namespace WebExpress.WebCore.WebJob.Model -{ - /// - /// Represents an job entry in the job execution directory. - /// - internal class ScheduleStaticItemValue - { - /// - /// The context associated with the job. - /// - public IJobContext JobContext { get; set; } - - /// - /// Returns the job instance. - /// - public IJob Instance { get; internal set; } - - /// - /// Returns the cancel token or null if not already created. - /// - public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource(); - } -} From 2d22bcdc83a142991677e4f5d8fa5c070ce0dc9f Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 25 Oct 2024 00:59:23 +0200 Subject: [PATCH 031/162] remove modulemanager - removed modulemanager from the codebase - refactored affected components to ensure smooth operation without modulemanager --- .../Fixture/UnitTestControlFixture.cs | 12 +- .../Manager/UnitTestApplication.cs | 36 +- .../Manager/UnitTestComponent.cs | 13 - .../Manager/UnitTestEventManager.cs | 18 +- .../Manager/UnitTestJobManager.cs | 41 +- .../Manager/UnitTestModule.cs | 212 -------- .../Manager/UnitTestPageManager.cs | 62 ++- .../Manager/UnitTestResourceManager.cs | 55 ++- .../Manager/UnitTestRestApiManager.cs | 58 ++- .../Manager/UnitTestSitemapManager.cs | 53 +- .../Manager/UnitTestStatusPageManager.cs | 72 ++- .../TestApplicationA.cs | 6 +- .../TestApplicationB.cs | 6 +- ...EventHandlerA1.cs => TestEventHandlerA.cs} | 4 +- ...EventHandlerB1.cs => TestEventHandlerB.cs} | 4 +- .../{TestJobA1.cs => TestJobA.cs} | 14 +- src/WebExpress.WebCore.Test/TestModuleA1.cs | 45 -- src/WebExpress.WebCore.Test/TestModuleA2.cs | 43 -- src/WebExpress.WebCore.Test/TestModuleAB1.cs | 51 -- src/WebExpress.WebCore.Test/TestModuleB1.cs | 50 -- src/WebExpress.WebCore.Test/TestModuleC1.cs | 50 -- src/WebExpress.WebCore.Test/TestModuleC2.cs | 47 -- .../{TestPageA1Y.cs => TestPageA.cs} | 9 +- .../{TestPageA1X.cs => TestPageB.cs} | 10 +- .../{TestPageA1Z.cs => TestPageC.cs} | 9 +- src/WebExpress.WebCore.Test/TestPlugin.cs | 6 +- .../{TestResourceA1X.cs => TestResourceA.cs} | 7 +- .../{TestResourceA1Y.cs => TestResourceB.cs} | 9 +- .../{TestResourceA2X.cs => TestResourceC.cs} | 7 +- .../{TestResourceAB1X.cs => TestResourceD.cs} | 7 +- .../{TestRestApiA1X.cs => TestRestApiA.cs} | 9 +- .../{TestRestApiA1Y.cs => TestRestApiB.cs} | 11 +- .../{TestRestApiA1Z.cs => TestRestApiC.cs} | 11 +- ...StatusPageB404.cs => TestStatusPage301.cs} | 7 +- ...StatusPageA400.cs => TestStatusPage400.cs} | 5 +- ...StatusPageA404.cs => TestStatusPage404.cs} | 5 +- ...StatusPageB500.cs => TestStatusPage500.cs} | 24 +- .../Uri/UnitTestUriAbsolute.cs | 3 +- .../Uri/UnitTestUriRelativeExtendedPath.cs | 3 +- src/WebExpress.WebCore/HttpServer.cs | 16 +- .../InternationalizationManager.cs | 7 + .../Internationalization/de | 54 +- .../Internationalization/en | 52 +- .../WebApplication/ApplicationContext.cs | 8 +- .../WebApplication/ApplicationManager.cs | 162 +++--- .../WebApplication/IApplicationContext.cs | 8 +- .../WebApplication/IApplicationManager.cs | 14 +- .../WebAttribute/ApplicationAttribute.cs | 18 +- .../WebAttribute/AssetPathAttribute.cs | 2 +- .../WebAttribute/ContextPathAttribute.cs | 2 +- .../WebAttribute/DataPathAttribute.cs | 2 +- .../WebAttribute/DescriptionAttribute.cs | 2 +- .../WebAttribute/IModuleAttribute.cs | 9 - .../WebAttribute/IconAttribute.cs | 2 +- .../WebAttribute/IdAttribute.cs | 2 +- .../WebAttribute/ModuleAttribute.cs | 21 - .../WebAttribute/NameAttribute.cs | 2 +- .../WebAttribute/OptionAttribute.cs | 42 -- .../WebAttribute/OptionalAttribute.cs | 19 - .../WebAttribute/ParentAttribute.cs | 2 +- .../WebAttribute/SegmentDoubleAttribute.cs | 12 - .../WebAttribute/SegmentStringAttribute.cs | 12 - .../WebAttribute/SegmentUIntAttribute.cs | 17 - .../WebComponent/ComponentHub.cs | 30 +- .../WebComponent/IComponentHub.cs | 14 +- .../WebComponent/IComponentManager.cs | 6 +- .../WebComponent/IEndpointManager.cs | 10 - .../WebEndpoint/EndpointManager.cs | 145 ++++++ .../EndpointRegistration.cs | 20 +- .../IEndpoint.cs | 4 +- .../IEndpointContext.cs | 9 +- .../WebEndpoint/IEndpointManager.cs | 57 +++ .../WebEvent/EventManager.cs | 188 ++++--- .../WebExpress.WebCore.csproj | 1 + src/WebExpress.WebCore/WebJob/Cron.cs | 32 +- src/WebExpress.WebCore/WebJob/IJobManager.cs | 12 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 355 ++++++++------ src/WebExpress.WebCore/WebLog/LogManager.cs | 7 + .../WebMessage/ResponseMovedPermanently.cs | 35 ++ src/WebExpress.WebCore/WebModule/IModule.cs | 16 - .../WebModule/IModuleContext.cs | 55 --- .../WebModule/IModuleManager.cs | 69 --- .../WebModule/Model/ModuleDictionary.cs | 13 - .../WebModule/Model/ModuleItem.cs | 271 ---------- src/WebExpress.WebCore/WebModule/Module.cs | 36 -- .../WebModule/ModuleContext.cs | 70 --- .../WebModule/ModuleItemInstance.cs | 22 - .../WebModule/ModuleManager.cs | 463 ------------------ .../WebPackage/PackageManager.cs | 7 + src/WebExpress.WebCore/WebPage/IPage.cs | 2 +- .../WebPage/IPageContext.cs | 2 +- .../WebPage/IPageManager.cs | 27 +- .../WebPage/Model/PageDictionary.cs | 138 +++++- .../WebPage/Model/PageItem.cs | 141 +----- src/WebExpress.WebCore/WebPage/PageContext.cs | 49 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 368 +++++++------- .../WebPlugin/IPluginManager.cs | 15 + .../WebPlugin/Model/PluginItem.cs | 5 + .../WebPlugin/PluginManager.cs | 64 ++- .../WebResource/IResource.cs | 2 +- .../WebResource/IResourceContext.cs | 2 +- .../WebResource/IResourceManager.cs | 27 +- .../WebResource/Model/ResourceDictionary.cs | 138 +++++- .../WebResource/Model/ResourceItem.cs | 152 +----- .../WebResource/ResourceContext.cs | 51 +- .../WebResource/ResourceManager.cs | 366 +++++++------- src/WebExpress.WebCore/WebRestAPI/IRestApi.cs | 2 +- .../WebRestAPI/IRestApiContext.cs | 2 +- .../WebRestAPI/IRestApiManager.cs | 27 +- .../WebRestAPI/Model/RestApiDictionary.cs | 138 +++++- .../WebRestAPI/Model/RestApiItem.cs | 138 +----- .../WebRestAPI/RestApiContext.cs | 56 +-- .../WebRestAPI/RestApiManager.cs | 371 +++++++------- .../WebSession/SessionManager.cs | 7 + .../WebSitemap/ISitemapManager.cs | 22 +- .../WebSitemap/Model/SitemapNode.cs | 2 +- .../WebSitemap/SearchResult.cs | 42 +- .../WebSitemap/SitemapManager.cs | 205 +------- .../WebStatusPage/IStatusPageManager.cs | 2 +- .../Model/StatusPageDictionary.cs | 9 +- .../WebStatusPage/StatusPageContext.cs | 9 + .../WebStatusPage/StatusPageManager.cs | 181 ++++--- src/WebExpress.WebCore/WebTask/TaskManager.cs | 7 + src/WebExpress.WebCore/WebUri/UriResource.cs | 20 +- 124 files changed, 2500 insertions(+), 3817 deletions(-) delete mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs rename src/WebExpress.WebCore.Test/{TestEventHandlerA1.cs => TestEventHandlerA.cs} (92%) rename src/WebExpress.WebCore.Test/{TestEventHandlerB1.cs => TestEventHandlerB.cs} (91%) rename src/WebExpress.WebCore.Test/{TestJobA1.cs => TestJobA.cs} (67%) delete mode 100644 src/WebExpress.WebCore.Test/TestModuleA1.cs delete mode 100644 src/WebExpress.WebCore.Test/TestModuleA2.cs delete mode 100644 src/WebExpress.WebCore.Test/TestModuleAB1.cs delete mode 100644 src/WebExpress.WebCore.Test/TestModuleB1.cs delete mode 100644 src/WebExpress.WebCore.Test/TestModuleC1.cs delete mode 100644 src/WebExpress.WebCore.Test/TestModuleC2.cs rename src/WebExpress.WebCore.Test/{TestPageA1Y.cs => TestPageA.cs} (89%) rename src/WebExpress.WebCore.Test/{TestPageA1X.cs => TestPageB.cs} (88%) rename src/WebExpress.WebCore.Test/{TestPageA1Z.cs => TestPageC.cs} (84%) rename src/WebExpress.WebCore.Test/{TestResourceA1X.cs => TestResourceA.cs} (88%) rename src/WebExpress.WebCore.Test/{TestResourceA1Y.cs => TestResourceB.cs} (84%) rename src/WebExpress.WebCore.Test/{TestResourceA2X.cs => TestResourceC.cs} (88%) rename src/WebExpress.WebCore.Test/{TestResourceAB1X.cs => TestResourceD.cs} (88%) rename src/WebExpress.WebCore.Test/{TestRestApiA1X.cs => TestRestApiA.cs} (90%) rename src/WebExpress.WebCore.Test/{TestRestApiA1Y.cs => TestRestApiB.cs} (89%) rename src/WebExpress.WebCore.Test/{TestRestApiA1Z.cs => TestRestApiC.cs} (89%) rename src/WebExpress.WebCore.Test/{TestStatusPageB404.cs => TestStatusPage301.cs} (86%) rename src/WebExpress.WebCore.Test/{TestStatusPageA400.cs => TestStatusPage400.cs} (90%) rename src/WebExpress.WebCore.Test/{TestStatusPageA404.cs => TestStatusPage404.cs} (88%) rename src/WebExpress.WebCore.Test/{TestStatusPageB500.cs => TestStatusPage500.cs} (64%) delete mode 100644 src/WebExpress.WebCore/WebAttribute/IModuleAttribute.cs delete mode 100644 src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs delete mode 100644 src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs delete mode 100644 src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs delete mode 100644 src/WebExpress.WebCore/WebComponent/IEndpointManager.cs create mode 100644 src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs rename src/WebExpress.WebCore/{WebSitemap => WebEndpoint}/EndpointRegistration.cs (50%) rename src/WebExpress.WebCore/{WebComponent => WebEndpoint}/IEndpoint.cs (69%) rename src/WebExpress.WebCore/{WebComponent => WebEndpoint}/IEndpointContext.cs (86%) create mode 100644 src/WebExpress.WebCore/WebEndpoint/IEndpointManager.cs create mode 100644 src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs delete mode 100644 src/WebExpress.WebCore/WebModule/IModule.cs delete mode 100644 src/WebExpress.WebCore/WebModule/IModuleContext.cs delete mode 100644 src/WebExpress.WebCore/WebModule/IModuleManager.cs delete mode 100644 src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs delete mode 100644 src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs delete mode 100644 src/WebExpress.WebCore/WebModule/Module.cs delete mode 100644 src/WebExpress.WebCore/WebModule/ModuleContext.cs delete mode 100644 src/WebExpress.WebCore/WebModule/ModuleItemInstance.cs delete mode 100644 src/WebExpress.WebCore/WebModule/ModuleManager.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index e4f2dd8..66966ea 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -4,10 +4,10 @@ using System.Net; using System.Reflection; using System.Text; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; @@ -179,7 +179,7 @@ public static RenderContext CrerateContextMock() { var request = CrerateRequestMock(); - return new RenderContext(CreratePageContextMock()?.ModuleContext?.ApplicationContext, request, []); + return new RenderContext(CreratePageContextMock()?.ApplicationContext, request, []); } /// @@ -188,13 +188,13 @@ public static RenderContext CrerateContextMock() /// A fake context for testing. public static PageContext CreratePageContextMock() { - var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IModuleContext)], null); + var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IApplicationContext)], null); - var moduleContext = WebEx.ComponentHub.ModuleManager.Modules - .Where(x => x.ModuleId == typeof(TestModuleA1).FullName.ToLower()) + var applicationContext = WebEx.ComponentHub.ApplicationManager.Applications + .Where(x => x.ApplicationId == typeof(TestApplicationA).FullName.ToLower()) .FirstOrDefault(); - var pageContext = (PageContext)ctorPageContext.Invoke([moduleContext]); + var pageContext = (PageContext)ctorPageContext.Invoke([applicationContext]); return pageContext; } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index ff13d0e..61be7f0 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -25,9 +25,9 @@ public void Register() pluginManager.Register(); Assert.Equal(3, componentHub.ApplicationManager.Applications.Count()); - Assert.Equal("webexpress.webcore.test.testapplicationa", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationA))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationb", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationB))?.ApplicationId); - Assert.Equal("webexpress.webcore.test.testapplicationc", componentHub.ApplicationManager.GetApplication(typeof(TestApplicationC))?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationa", componentHub.ApplicationManager.GetApplications(typeof(TestApplicationA)).FirstOrDefault()?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationb", componentHub.ApplicationManager.GetApplications(typeof(TestApplicationB)).FirstOrDefault()?.ApplicationId); + Assert.Equal("webexpress.webcore.test.testapplicationc", componentHub.ApplicationManager.GetApplications(typeof(TestApplicationC)).FirstOrDefault()?.ApplicationId); } /// @@ -58,7 +58,7 @@ public void Id(Type applicationType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(id, application.ApplicationId); @@ -75,7 +75,7 @@ public void Name(Type applicationType, string name) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(name, application.ApplicationName); @@ -92,7 +92,7 @@ public void Description(Type applicationType, string description) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(description, application.Description); @@ -102,14 +102,14 @@ public void Description(Type applicationType, string description) /// Test the icon property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/aca/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationB), "/acb/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationA), "/appa/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationB), "/appb/assets/img/Logo.png")] [InlineData(typeof(TestApplicationC), "/assets/img/Logo.png")] public void Icon(Type applicationType, string icon) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(icon, application.Icon); @@ -119,14 +119,14 @@ public void Icon(Type applicationType, string icon) /// Test the context path property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/aca")] - [InlineData(typeof(TestApplicationB), "/acb")] + [InlineData(typeof(TestApplicationA), "/appa")] + [InlineData(typeof(TestApplicationB), "/appb")] [InlineData(typeof(TestApplicationC), "/")] public void ContextPath(Type applicationType, string contextPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(contextPath, application.ContextPath); @@ -136,14 +136,14 @@ public void ContextPath(Type applicationType, string contextPath) /// Test the asset path property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/aaa")] - [InlineData(typeof(TestApplicationB), "/aab")] + [InlineData(typeof(TestApplicationA), "/asseta")] + [InlineData(typeof(TestApplicationB), "/assetb")] [InlineData(typeof(TestApplicationC), "/")] public void AssetPath(Type applicationType, string assetPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(assetPath, application.AssetPath); @@ -153,14 +153,14 @@ public void AssetPath(Type applicationType, string assetPath) /// Test the data path property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/ada")] - [InlineData(typeof(TestApplicationB), "/adb")] + [InlineData(typeof(TestApplicationA), "/dataa")] + [InlineData(typeof(TestApplicationB), "/datab")] [InlineData(typeof(TestApplicationC), "/")] public void DataPath(Type applicationType, string dataPath) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution Assert.Equal(dataPath, application.DataPath); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs index 79fe791..a02e06d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs @@ -33,18 +33,5 @@ public void ApplicationManager() // test execution Assert.NotNull(componentHub.ApplicationManager); } - - /// - /// Test the module manager property of the component manager. - /// - [Fact] - public void ModuleManager() - { - // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); - - // test execution - Assert.NotNull(componentHub.ModuleManager); - } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs index 1800c83..0911bca 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(2, componentHub.EventManager.EventHandlers.Count()); + Assert.Equal(6, componentHub.EventManager.EventHandlers.Count()); } /// @@ -57,15 +57,15 @@ public void IsIComponentManager() /// Test the id property of the event handler. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestEventA), "webexpress.webcore.test.testeventhandlera1")] - [InlineData(typeof(TestApplicationB), typeof(TestEventB), "webexpress.webcore.test.testeventhandlerb1")] - [InlineData(typeof(TestApplicationA), typeof(TestEventB), null)] - [InlineData(typeof(TestApplicationB), typeof(TestEventA), null)] + [InlineData(typeof(TestApplicationA), typeof(TestEventA), "webexpress.webcore.test.testeventhandlera")] + [InlineData(typeof(TestApplicationA), typeof(TestEventB), "webexpress.webcore.test.testeventhandlerb")] + [InlineData(typeof(TestApplicationB), typeof(TestEventA), "webexpress.webcore.test.testeventhandlera")] + [InlineData(typeof(TestApplicationB), typeof(TestEventB), "webexpress.webcore.test.testeventhandlerb")] public void Id(Type applicationType, Type eventType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution var eventHandlers = componentHub.EventManager.GetEventHandlers(application, eventType); @@ -84,14 +84,14 @@ public void Id(Type applicationType, Type eventType, string id) /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestEventA), "webexpress.webcore.test.testeventa")] + [InlineData(typeof(TestApplicationA), typeof(TestEventB), "webexpress.webcore.test.testeventb")] + [InlineData(typeof(TestApplicationB), typeof(TestEventA), "webexpress.webcore.test.testeventa")] [InlineData(typeof(TestApplicationB), typeof(TestEventB), "webexpress.webcore.test.testeventb")] - [InlineData(typeof(TestApplicationA), typeof(TestEventB), null)] - [InlineData(typeof(TestApplicationB), typeof(TestEventA), null)] public void EventId(Type applicationType, Type eventType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution var eventHandlers = componentHub.EventManager.GetEventHandlers(application, eventType); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs index e9cdd93..36927fd 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(1, componentHub.JobManager.Jobs.Count()); + Assert.Equal(3, componentHub.JobManager.Jobs.Count()); } /// @@ -73,26 +73,41 @@ public void IsIContext() /// Test the id property of the job. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webexpress.webcore.test.testresourcea1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "webexpress.webcore.test.testresourcea1y")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webexpress.webcore.test.testresourcea2x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestPageA1X), null)] + [InlineData(typeof(TestApplicationA), typeof(TestJobA), "webexpress.webcore.test.testjoba")] + [InlineData(typeof(TestApplicationB), typeof(TestJobA), "webexpress.webcore.test.testjoba")] + [InlineData(typeof(TestApplicationC), typeof(TestJobA), "webexpress.webcore.test.testjoba")] - public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + public void Id(Type applicationType, Type jobType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var resource = componentHub.ResourceManager.GetResorce(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var job = componentHub.JobManager.GetJob(application, jobType); // test execution - Assert.Equal(id, resource?.EndpointId); + Assert.Equal(id, job?.JobId); } + /// + /// Test the cron property of the job. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestJobA), 50, 8, 31, new[] { 1, 2 }, 0)] + [InlineData(typeof(TestApplicationB), typeof(TestJobA), 50, 8, 31, new[] { 1, 2 }, 0)] + [InlineData(typeof(TestApplicationC), typeof(TestJobA), 50, 8, 31, new[] { 1, 2 }, 0)] + public void Cron(Type applicationType, Type jobType, int minute, int hour, int day, int[] month, int weekday) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var job = componentHub.JobManager.GetJob(application, jobType); - - + // test execution + Assert.Equal(minute, job?.Cron.Minute.FirstOrDefault() ?? -1); + Assert.Equal(hour, job?.Cron.Hour.FirstOrDefault() ?? -1); + Assert.Equal(day, job?.Cron.Day.FirstOrDefault() ?? -1); + Assert.True(month.SequenceEqual(job?.Cron.Month ?? [])); + Assert.Equal(weekday, job?.Cron.Weekday.FirstOrDefault() ?? -1); + } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs deleted file mode 100644 index 5dd1064..0000000 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestModule.cs +++ /dev/null @@ -1,212 +0,0 @@ -using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.Test.Manager -{ - /// - /// Test the module manager. - /// - [Collection("NonParallelTests")] - public class UnitTestModule - { - /// - /// Test the register function of the module manager. - /// - [Fact] - public void Register() - { - // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); - var pluginManager = componentHub.PluginManager as PluginManager; - - // test execution - pluginManager.Register(); - - Assert.Equal(7, componentHub.ModuleManager.Modules.Count()); - } - - /// - /// Test the remove function of the module manager. - /// - [Fact] - public void Remove() - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var moduleManager = componentHub.ModuleManager as ModuleManager; - var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); - - // test execution - moduleManager.Remove(plugin); - - Assert.Empty(componentHub.ModuleManager.Modules); - } - - /// - /// Test the id property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "webexpress.webcore.test.testmodulea1")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "webexpress.webcore.test.testmodulea2")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "webexpress.webcore.test.testmoduleb1")] - public void Id(Type applicationType, Type moduleType, string id) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - - // test execution - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - Assert.Equal(id, module.ModuleId); - } - - /// - /// Test the name property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.namea1")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.namea2")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "testmoduleb1")] - public void Name(Type applicationType, Type moduleType, string name) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - - // test execution - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - Assert.Equal(name, module.ModuleName); - } - - /// - /// Test the description property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "module.descriptiona1")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "module.descriptiona2")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "")] - public void Description(Type applicationType, Type moduleType, string description) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - - // test execution - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - Assert.Equal(description, module.Description); - } - - /// - /// Test the icon property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/aca/mca/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aca/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/acb/mcb")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/aca/mcab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/acb/mcab")] - public void Icon(Type applicationType, Type moduleType, string icon) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - // test execution - Assert.Equal(icon, module.Icon); - } - - /// - /// Test the context path property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aca")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/acb/mcb")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/aca/mcab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/acb/mcab")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mcc")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] - public void ContextPath(Type applicationType, Type moduleType, string contextPath) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - // test execution - Assert.Equal(contextPath, module.ContextPath); - } - - /// - /// Test the asset path property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/maa")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/aaa")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/mab")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/maab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/maab")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mac")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] - public void AssetPath(Type applicationType, Type moduleType, string assetPath) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - // test execution - Assert.Equal(assetPath, module.AssetPath); - } - - /// - /// Test the data path property of the module. - /// - [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), "/mda")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), "/ada")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleB1), "/mdb")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), "/mdab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), "/mdab")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC1), "/mdc")] - [InlineData(typeof(TestApplicationC), typeof(TestModuleC2), "/")] - public void DataPath(Type applicationType, Type moduleType, string dataPath) - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - - // test execution - Assert.Equal(dataPath, module.DataPath); - } - - /// - /// Tests whether the module manager implements interface IComponentManager. - /// - [Fact] - public void IsIComponentManager() - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - - // test execution - Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ModuleManager.GetType())); - } - - /// - /// Tests whether the module context implements interface IContext. - /// - [Fact] - public void IsIContext() - { - // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - - // test execution - foreach (var module in componentHub.ModuleManager.Modules) - { - Assert.True(typeof(IContext).IsAssignableFrom(module.GetType()), $"Module context {module.GetType().Name} does not implement IContext."); - } - } - } -} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index b4080d8..cd422bf 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(3, componentHub.PageManager.Pages.Count()); + Assert.Equal(9, componentHub.PageManager.Pages.Count()); } /// @@ -44,15 +44,21 @@ public void Remove() /// Test the id property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webexpress.webcore.test.testpagea1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "webexpress.webcore.test.testpagea1y")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "webexpress.webcore.test.testpagea1z")] - public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + [InlineData(typeof(TestApplicationA), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + [InlineData(typeof(TestApplicationB), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + [InlineData(typeof(TestApplicationB), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + [InlineData(typeof(TestApplicationC), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + [InlineData(typeof(TestApplicationC), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + [InlineData(typeof(TestApplicationC), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + public void Id(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var page = componentHub.PageManager.GetPage(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, page.EndpointId); @@ -62,16 +68,22 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string /// Test the title property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "webindex:homepage.label")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "webindex:homepage.label")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "webindex:homepage.label")] - - public void Title(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestPageA), "webindex:pagea.label")] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), "webindex:pageb.label")] + [InlineData(typeof(TestApplicationA), typeof(TestPageC), "webindex:pagec.label")] + [InlineData(typeof(TestApplicationB), typeof(TestPageA), "webindex:pagea.label")] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), "webindex:pageb.label")] + [InlineData(typeof(TestApplicationB), typeof(TestPageC), "webindex:pagec.label")] + [InlineData(typeof(TestApplicationC), typeof(TestPageA), "webindex:pagea.label")] + [InlineData(typeof(TestApplicationC), typeof(TestPageB), "webindex:pageb.label")] + [InlineData(typeof(TestApplicationC), typeof(TestPageC), "webindex:pagec.label")] + + public void Title(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var page = componentHub.PageManager.GetPage(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, page.PageTitle); @@ -81,15 +93,21 @@ public void Title(Type applicationType, Type moduleType, Type resourceType, stri /// Test the context path property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1X), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Y), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestPageA1Z), "/aca/mca")] - public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestPageA), "/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/appa/resa")] + [InlineData(typeof(TestApplicationA), typeof(TestPageC), "/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestPageA), "/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/appb/resa")] + [InlineData(typeof(TestApplicationB), typeof(TestPageC), "/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/")] + [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/resa")] + [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/")] + public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var page = componentHub.PageManager.GetPage(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, page.ContextPath); @@ -118,9 +136,9 @@ public void IsIContext() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - foreach (var application in componentHub.PageManager.Pages) + foreach (var pages in componentHub.PageManager.Pages) { - Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Page context {application.GetType().Name} does not implement IContext."); + Assert.True(typeof(IContext).IsAssignableFrom(pages.GetType()), $"Page context {pages.GetType().Name} does not implement IContext."); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index a827d4d..b7083c5 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -20,8 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - // resources (3 unique + 2 ambiguous) - Assert.Equal(5, componentHub.ResourceManager.Resources.Count()); + Assert.Equal(12, componentHub.ResourceManager.Resources.Count()); } /// @@ -45,19 +44,24 @@ public void Remove() /// Test the id property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "webexpress.webcore.test.testresourcea1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "webexpress.webcore.test.testresourcea1y")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "webexpress.webcore.test.testresourcea2x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "webexpress.webcore.test.testresourceab1x")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestPageA1X), null)] - - public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] + public void Id(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var resource = componentHub.ResourceManager.GetResorce(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, resource?.EndpointId); @@ -67,18 +71,25 @@ public void Id(Type applicationType, Type moduleType, Type resourceType, string /// Test the context path property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1X), "/aca/mca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestResourceA1Y), "/aca/mca/a1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA2), typeof(TestResourceA2X), "/aca")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/aca/mcab")] - [InlineData(typeof(TestApplicationB), typeof(TestModuleAB1), typeof(TestResourceAB1X), "/acb/mcab")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "/appa/resa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "/appb/resa")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "/")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "/resa")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/")] - public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var resource = componentHub.ResourceManager.GetResorce(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, resource.ContextPath); @@ -107,9 +118,9 @@ public void IsIContext() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - foreach (var application in componentHub.ResourceManager.Resources) + foreach (var resources in componentHub.ResourceManager.Resources) { - Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Resource context {application.GetType().Name} does not implement IContext."); + Assert.True(typeof(IContext).IsAssignableFrom(resources.GetType()), $"Resource context {resources.GetType().Name} does not implement IContext."); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 16679f5..2e64f9a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(3, componentHub.RestApiManager.RestApis.Count()); + Assert.Equal(9, componentHub.RestApiManager.RestApis.Count()); } /// @@ -44,55 +44,67 @@ public void Remove() /// Test the id property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "webexpress.webcore.test.testrestapia1x")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "webexpress.webcore.test.testrestapia1y")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "webexpress.webcore.test.testrestapia1z")] - public void Id(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] + public void Id(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, api.EndpointId); + Assert.Equal(id, api?.EndpointId); } /// /// Test the context path property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), "/aca/mca/1")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), "/aca/mca/1/ra1x/2")] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), "/aca/mca/1/ra1x/2/ra1y/3")] - public void ContextPath(Type applicationType, Type moduleType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "/appa/1")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "/appa/1/apia/2")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "/appa/1/apia/2/apib/3")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "/appb/1")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "/appb/1/apia/2")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "/appb/1/apia/2/apib/3")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/1")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/1/apia/2")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/1/apia/2/apib/3")] + public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, api.ContextPath); + Assert.Equal(id, api?.ContextPath); } /// /// Test the context path property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), CrudMethod.POST)] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1X), CrudMethod.GET)] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Y), CrudMethod.GET)] - [InlineData(typeof(TestApplicationA), typeof(TestModuleA1), typeof(TestRestApiA1Z), CrudMethod.GET)] - public void Method(Type applicationType, Type moduleType, Type resourceType, CrudMethod method) + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), CrudMethod.POST)] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), CrudMethod.GET)] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), CrudMethod.GET)] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), CrudMethod.GET)] + public void Method(Type applicationType, Type resourceType, CrudMethod method) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var module = componentHub.ModuleManager.GetModule(applicationType, moduleType); - var api = componentHub.RestApiManager.GetRestApi(module, resourceType); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Contains(method, api.Methods); + Assert.Contains(method, api?.Methods); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index b75ee30..dced294 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,22 +22,37 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(22, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(42, componentManager.SitemapManager.SiteMap.Count()); } /// /// Test the SearchResource function of the sitemap. /// [Theory] - [InlineData("http://localhost:8080/aca/mca/a1x", "webexpress.webcore.test.testresourcea1x")] - [InlineData("http://localhost:8080/aca/mca/a1x/a1y", "webexpress.webcore.test.testresourcea1y")] - [InlineData("http://localhost:8080/aca/a2x", "webexpress.webcore.test.testresourcea2x")] - [InlineData("http://localhost:8080/aca/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] - [InlineData("http://localhost:8080/acb/mcab/ab1x", "webexpress.webcore.test.testresourceab1x")] - [InlineData("http://localhost:8080/aca/mca/pa1x", "webexpress.webcore.test.testpagea1x")] - [InlineData("http://localhost:8080/aca/mca/1/ra1x", "webexpress.webcore.test.testrestapia1x")] - [InlineData("http://localhost:8080/aca/mca/1/ra1x/2/ra1y", "webexpress.webcore.test.testrestapia1y")] - [InlineData("http://localhost:8080/aca/mca/1/ra1x/2/ra1y/3/ra1z", "webexpress.webcore.test.testrestapia1z")] + [InlineData("http://localhost:8080/appa/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/appa/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/appa/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/appa/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/appb/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/appb/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/appb/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/appb/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/appa/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/appa/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/appa/pagec", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/appb/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/appb/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/appb/pagec", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/pagec", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/appa/1/apia", "webexpress.webcore.test.testrestapia")] + [InlineData("http://localhost:8080/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] + [InlineData("http://localhost:8080/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) @@ -55,22 +70,22 @@ public void SearchResource(string uri, string id) HttpContext = context }); - searchResult.Process(UnitTestControlFixture.CrerateRequestMock()); + componentHub.EndpointManager.HandleRequest(UnitTestControlFixture.CrerateRequestMock(), searchResult.EndpointContext); - Assert.Equal(id, searchResult.EndpointId); + Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); } /// /// Test the GetUri function of the sitemap. /// [Theory] - [InlineData(typeof(TestResourceA1X), "/aca/mca/a1x")] - [InlineData(typeof(TestResourceA1Y), "/aca/mca/a1x/a1y")] - [InlineData(typeof(TestResourceA2X), "/aca/a2x")] - [InlineData(typeof(TestResourceAB1X), "/aca/mcab/ab1x")] - [InlineData(typeof(TestPageA1X), "/aca/mca/pa1x")] - [InlineData(typeof(TestPageA1Y), "/aca/mca/pa1y")] - [InlineData(typeof(TestPageA1Z), "/aca/mca/pa1z")] + [InlineData(typeof(TestResourceA), "/appa/resa")] + [InlineData(typeof(TestResourceB), "/appa/resa/resb")] + [InlineData(typeof(TestResourceC), "/appa/resc")] + [InlineData(typeof(TestResourceD), "/appa/resd")] + [InlineData(typeof(TestPageA), "/appa/pagea")] + [InlineData(typeof(TestPageB), "/appa/resa/pageb")] + [InlineData(typeof(TestPageC), "/appa/pagec")] public void GetUri(Type resourceType, string expected) { diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 01d61f6..bcf0806 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(3, componentHub.StatusPageManager.StatusPages.Count()); + Assert.Equal(12, componentHub.StatusPageManager.StatusPages.Count()); } /// @@ -44,14 +44,16 @@ public void Remove() /// Test the id property of the status page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "webexpress.webcore.test.teststatuspagea400")] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), "webexpress.webcore.test.teststatuspagea404")] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), "webexpress.webcore.test.teststatuspageb404")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage301), "webexpress.webcore.test.teststatuspage301")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), "webexpress.webcore.test.teststatuspage400")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), "webexpress.webcore.test.teststatuspage404")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), "webexpress.webcore.test.teststatuspage500")] + public void Id(Type applicationType, Type statusPageType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); // test execution @@ -62,13 +64,23 @@ public void Id(Type applicationType, Type statusPageType, string id) /// Test the title property of the status page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "webindex:homepage.label")] - + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage301), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage301), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage400), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage404), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage500), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage301), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), "webindex:homepage.label")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), "webindex:homepage.label")] public void Title(Type applicationType, Type resourceType, string id) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, resourceType); // test execution @@ -79,16 +91,23 @@ public void Title(Type applicationType, Type resourceType, string id) /// Test the id property of the status page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), 400)] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), 404)] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), 404)] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageB500), null)] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB500), null)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage301), 301)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), 400)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), 404)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), 500)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage301), 301)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage400), 400)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage404), 404)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage500), 500)] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage301), 301)] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), 400)] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), 404)] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), 500)] public void Code(Type applicationType, Type statusPageType, int? code) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); // test execution @@ -99,16 +118,23 @@ public void Code(Type applicationType, Type statusPageType, int? code) /// Test the icon property of the status page. ///
[Theory] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA400), "/aca/webexpress/icon.png")] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageA404), "/aca/webexpress/icon.png")] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB404), null)] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPageB500), null)] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPageB500), null)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage301), null)] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), "/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), "/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), "/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage301), null)] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage400), "/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage404), "/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage500), "/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage301), null)] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), "/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), "/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), "/webexpress/icon.png")] public void Icon(Type applicationType, Type statusPageType, string icon) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); // test execution @@ -128,7 +154,7 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); // test execution @@ -140,12 +166,12 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect ///
[Theory] [InlineData(typeof(TestApplicationA), 400, "content", "content", 78)] - [InlineData(typeof(TestApplicationA), 500, "content", "content", 7)] + [InlineData(typeof(TestApplicationA), 500, "content", "content", 78)] public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected, int length) { // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var application = componentHub.ApplicationManager.GetApplication(applicationType); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); // test execution diff --git a/src/WebExpress.WebCore.Test/TestApplicationA.cs b/src/WebExpress.WebCore.Test/TestApplicationA.cs index ce03bba..f0b51c0 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationA.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationA.cs @@ -9,9 +9,9 @@ namespace WebExpress.WebCore.Test [Name("TestApplicationA")] [Description("application.description")] [Icon("/assets/img/Logo.png")] - [ContextPath("/aca")] - [AssetPath("/aaa")] - [DataPath("/ada")] + [ContextPath("/appa")] + [AssetPath("/asseta")] + [DataPath("/dataa")] [Dependency("webexpress.webui")] public sealed class TestApplicationA : IApplication { diff --git a/src/WebExpress.WebCore.Test/TestApplicationB.cs b/src/WebExpress.WebCore.Test/TestApplicationB.cs index ffdc9ec..5af1ea7 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationB.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationB.cs @@ -9,9 +9,9 @@ namespace WebExpress.WebCore.Test [Name("TestApplicationB")] [Description("application.description")] [Icon("/assets/img/Logo.png")] - [ContextPath("/acb")] - [AssetPath("/aab")] - [DataPath("/adb")] + [ContextPath("/appb")] + [AssetPath("/assetb")] + [DataPath("/datab")] [Dependency("webexpress.webui")] public sealed class TestApplicationB : IApplication { diff --git a/src/WebExpress.WebCore.Test/TestEventHandlerA1.cs b/src/WebExpress.WebCore.Test/TestEventHandlerA.cs similarity index 92% rename from src/WebExpress.WebCore.Test/TestEventHandlerA1.cs rename to src/WebExpress.WebCore.Test/TestEventHandlerA.cs index 972f720..c93ae04 100644 --- a/src/WebExpress.WebCore.Test/TestEventHandlerA1.cs +++ b/src/WebExpress.WebCore.Test/TestEventHandlerA.cs @@ -9,14 +9,14 @@ namespace WebExpress.WebCore.Test ///
[Application()] [Event] - public sealed class TestEventHandlerA1 : IEventHandler + public sealed class TestEventHandlerA : IEventHandler { /// /// Initialization of the event. /// /// The event handler context, for testing the injection. /// The application manager, for testing the injection. - private TestEventHandlerA1(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) + private TestEventHandlerA(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) { // test the injection if (eventHandlerContext == null) diff --git a/src/WebExpress.WebCore.Test/TestEventHandlerB1.cs b/src/WebExpress.WebCore.Test/TestEventHandlerB.cs similarity index 91% rename from src/WebExpress.WebCore.Test/TestEventHandlerB1.cs rename to src/WebExpress.WebCore.Test/TestEventHandlerB.cs index c8767cf..542d528 100644 --- a/src/WebExpress.WebCore.Test/TestEventHandlerB1.cs +++ b/src/WebExpress.WebCore.Test/TestEventHandlerB.cs @@ -9,14 +9,14 @@ namespace WebExpress.WebCore.Test ///
[Application()] [Event] - public sealed class TestEventHandlerB1 : IEventHandler + public sealed class TestEventHandlerB : IEventHandler { /// /// Initialization of the event. /// /// The event handler context, for testing the injection. /// The application manager, for testing the injection. - private TestEventHandlerB1(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) + private TestEventHandlerB(IEventHandlerContext eventHandlerContext, IApplicationManager applicationManager) { // test the injection if (eventHandlerContext == null) diff --git a/src/WebExpress.WebCore.Test/TestJobA1.cs b/src/WebExpress.WebCore.Test/TestJobA.cs similarity index 67% rename from src/WebExpress.WebCore.Test/TestJobA1.cs rename to src/WebExpress.WebCore.Test/TestJobA.cs index a065a58..8794b38 100644 --- a/src/WebExpress.WebCore.Test/TestJobA1.cs +++ b/src/WebExpress.WebCore.Test/TestJobA.cs @@ -6,20 +6,14 @@ namespace WebExpress.WebCore.Test /// /// A dummy job for testing purposes. /// - [Application()] - [Name("module.namea1")] - [Description("module.descriptiona1")] - [Icon("/assets/img/Logo.png")] - [ContextPath("/mca")] - [AssetPath("/maa")] - [DataPath("/mda")] - public sealed class TestJobA1 : IJob + [Job("50", "8", "31", "1-2", "Saturday")] + public sealed class TestJobA : IJob { /// /// Initialization of the job. /// - /// The module context, for testing the injection. - private TestJobA1(IJobContext jobContext) + /// The job context, for testing the injection. + private TestJobA(IJobContext jobContext) { // test the injection if (jobContext == null) diff --git a/src/WebExpress.WebCore.Test/TestModuleA1.cs b/src/WebExpress.WebCore.Test/TestModuleA1.cs deleted file mode 100644 index ee91ae4..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleA1.cs +++ /dev/null @@ -1,45 +0,0 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - [Name("module.namea1")] - [Description("module.descriptiona1")] - [Icon("/assets/img/Logo.png")] - [ContextPath("/mca")] - [AssetPath("/maa")] - [DataPath("/mda")] - public sealed class TestModuleA1 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - private TestModuleA1(IModuleContext moduleContext) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestModuleA2.cs b/src/WebExpress.WebCore.Test/TestModuleA2.cs deleted file mode 100644 index 4a02b4a..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleA2.cs +++ /dev/null @@ -1,43 +0,0 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - [Name("module.namea2")] - [Description("module.descriptiona2")] - [ContextPath(null)] - [Icon("/assets/img/Logo.png")] - public sealed class TestModuleA2 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - private TestModuleA2(IModuleContext moduleContext) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestModuleAB1.cs b/src/WebExpress.WebCore.Test/TestModuleAB1.cs deleted file mode 100644 index cb216b3..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleAB1.cs +++ /dev/null @@ -1,51 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - [Application()] - [ContextPath("/mcab")] - [AssetPath("/maab")] - [DataPath("/mdab")] - public sealed class TestModuleAB1 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - /// The application manager, for testing the injection. - private TestModuleAB1(IModuleContext moduleContext, ApplicationManager applicationManager) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - - // test the injection - if (applicationManager == null) - { - throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestModuleB1.cs b/src/WebExpress.WebCore.Test/TestModuleB1.cs deleted file mode 100644 index 5f9cd0a..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleB1.cs +++ /dev/null @@ -1,50 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - [ContextPath("/mcb")] - [AssetPath("/mab")] - [DataPath("/mdb")] - public sealed class TestModuleB1 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - /// The application manager, for testing the injection. - private TestModuleB1(IModuleContext moduleContext, ApplicationManager applicationManager) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - - // test the injection - if (applicationManager == null) - { - throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestModuleC1.cs b/src/WebExpress.WebCore.Test/TestModuleC1.cs deleted file mode 100644 index 0947444..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleC1.cs +++ /dev/null @@ -1,50 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - [ContextPath("/mcc")] - [AssetPath("/mac")] - [DataPath("/mdc")] - public sealed class TestModuleC1 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - /// The application manager, for testing the injection. - private TestModuleC1(IModuleContext moduleContext, ApplicationManager applicationManager) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - - // test the injection - if (applicationManager == null) - { - throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestModuleC2.cs b/src/WebExpress.WebCore.Test/TestModuleC2.cs deleted file mode 100644 index ae4a9cd..0000000 --- a/src/WebExpress.WebCore.Test/TestModuleC2.cs +++ /dev/null @@ -1,47 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.Test -{ - /// - /// A dummy module for testing purposes. - /// - [Application()] - public sealed class TestModuleC2 : IModule - { - /// - /// Initialization of the module. - /// - /// The module context, for testing the injection. - /// The application manager, for testing the injection. - private TestModuleC2(IModuleContext moduleContext, ApplicationManager applicationManager) - { - // test the injection - if (moduleContext == null) - { - throw new ArgumentNullException(nameof(moduleContext), "Parameter cannot be null or empty."); - } - - // test the injection - if (applicationManager == null) - { - throw new ArgumentNullException(nameof(applicationManager), "Parameter cannot be null or empty."); - } - } - - /// - /// Called when the plugin starts working. The call is concurrent. - /// - public void Run() - { - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestPageA1Y.cs b/src/WebExpress.WebCore.Test/TestPageA.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestPageA1Y.cs rename to src/WebExpress.WebCore.Test/TestPageA.cs index 2ac576c..45dd6c8 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestPageA.cs @@ -6,11 +6,10 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:homepage.label")] - [Segment("pa1y", "webindex:homepage.label")] + [Title("webindex:pagea.label")] + [Segment("pagea", "webindex:homepage.label")] [ContextPath(null)] - [Module] - public sealed class TestPageA1Y : IPage + public sealed class TestPageA : IPage { /// /// Returns or sets the title of the page. @@ -26,7 +25,7 @@ public sealed class TestPageA1Y : IPage /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - public TestPageA1Y(IPageContext pageContext) + public TestPageA(IPageContext pageContext) { PageContext = pageContext; diff --git a/src/WebExpress.WebCore.Test/TestPageA1X.cs b/src/WebExpress.WebCore.Test/TestPageB.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestPageA1X.cs rename to src/WebExpress.WebCore.Test/TestPageB.cs index 3ae67a6..9de34b5 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1X.cs +++ b/src/WebExpress.WebCore.Test/TestPageB.cs @@ -6,11 +6,11 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:homepage.label")] - [Segment("pa1x", "webindex:homepage.label")] + [Title("webindex:pageb.label")] + [Segment("pageb", "webindex:homepage.label")] [ContextPath(null)] - [Module] - public sealed class TestPageA1X : IPage + [Parent] + public sealed class TestPageB : IPage { /// /// Returns or sets the title of the page. @@ -26,7 +26,7 @@ public sealed class TestPageA1X : IPage /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - public TestPageA1X(IPageContext pageContext) + public TestPageB(IPageContext pageContext) { PageContext = pageContext; diff --git a/src/WebExpress.WebCore.Test/TestPageA1Z.cs b/src/WebExpress.WebCore.Test/TestPageC.cs similarity index 84% rename from src/WebExpress.WebCore.Test/TestPageA1Z.cs rename to src/WebExpress.WebCore.Test/TestPageC.cs index a70603b..cc90bdb 100644 --- a/src/WebExpress.WebCore.Test/TestPageA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestPageC.cs @@ -6,17 +6,16 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:homepage.label")] - [Segment("pa1z", "webindex:homepage.label")] + [Title("webindex:pagec.label")] + [Segment("pagec", "webindex:homepage.label")] [ContextPath(null)] - [Module] - public sealed class TestPageA1Z : Page + public sealed class TestPageC : Page { /// /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - private TestPageA1Z(IPageContext pageContext) + private TestPageC(IPageContext pageContext) { // test the injection if (pageContext == null) diff --git a/src/WebExpress.WebCore.Test/TestPlugin.cs b/src/WebExpress.WebCore.Test/TestPlugin.cs index 7b65278..637db78 100644 --- a/src/WebExpress.WebCore.Test/TestPlugin.cs +++ b/src/WebExpress.WebCore.Test/TestPlugin.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.Test @@ -9,6 +10,9 @@ namespace WebExpress.WebCore.Test [Name("TestPlugin")] [Description("plugin.description")] [Icon("/assets/img/Logo.png")] + [Application()] + [Application()] + [Application()] public sealed class TestPlugin : IPlugin { /// diff --git a/src/WebExpress.WebCore.Test/TestResourceA1X.cs b/src/WebExpress.WebCore.Test/TestResourceA.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestResourceA1X.cs rename to src/WebExpress.WebCore.Test/TestResourceA.cs index 79786bc..b2712e2 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceA.cs @@ -8,16 +8,15 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. /// [Title("webindex:resourcea1x.label")] - [Segment("a1x", "webindex:homepage.label")] + [Segment("resa", "webindex:homepage.label")] [ContextPath(null)] - [Module] - public sealed class TestResourceA1X : IResource + public sealed class TestResourceA : IResource { /// /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The context of the resource. - public TestResourceA1X(IResourceContext resourceContext) + public TestResourceA(IResourceContext resourceContext) { // test the injection if (resourceContext == null) diff --git a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs b/src/WebExpress.WebCore.Test/TestResourceB.cs similarity index 84% rename from src/WebExpress.WebCore.Test/TestResourceA1Y.cs rename to src/WebExpress.WebCore.Test/TestResourceB.cs index 670f71a..39c3f96 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestResourceB.cs @@ -7,15 +7,14 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Segment("a1y", "webindex:homepage.label")] - [Parent] - [Module] - public sealed class TestResourceA1Y : IResource + [Segment("resb", "webindex:homepage.label")] + [Parent] + public sealed class TestResourceB : IResource { /// /// Initialization of the resource. Here, for example, managed resources can be loaded. /// - public TestResourceA1Y() + public TestResourceB() { } diff --git a/src/WebExpress.WebCore.Test/TestResourceA2X.cs b/src/WebExpress.WebCore.Test/TestResourceC.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestResourceA2X.cs rename to src/WebExpress.WebCore.Test/TestResourceC.cs index fa7ba51..6521a77 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA2X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceC.cs @@ -8,17 +8,16 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:resourcea2x.label")] - [Segment("a2x", "webindex:homepage.label")] + [Segment("resc", "webindex:homepage.label")] [ContextPath(null)] - [Module] - public sealed class TestResourceA2X : IResource + public sealed class TestResourceC : IResource { /// /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The resource manager. /// The context of the resource. - public TestResourceA2X(IResourceManager resourceManager, IResourceContext resourceContext) + public TestResourceC(IResourceManager resourceManager, IResourceContext resourceContext) { // test the injection if (resourceManager == null) diff --git a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs b/src/WebExpress.WebCore.Test/TestResourceD.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestResourceAB1X.cs rename to src/WebExpress.WebCore.Test/TestResourceD.cs index 2d102f5..0684e15 100644 --- a/src/WebExpress.WebCore.Test/TestResourceAB1X.cs +++ b/src/WebExpress.WebCore.Test/TestResourceD.cs @@ -8,16 +8,15 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:resourceab1x.label")] - [Segment("ab1x", "webindex:homepage.label")] - [Module] - public sealed class TestResourceAB1X : IResource + [Segment("resd", "webindex:homepage.label")] + public sealed class TestResourceD : IResource { /// /// Initialization of the resource. Here, for example, managed resources can be loaded. /// /// The context of the resource. /// The resource manager. - public TestResourceAB1X(IResourceContext resourceContext, IResourceManager resourceManager) + public TestResourceD(IResourceContext resourceContext, IResourceManager resourceManager) { // test the injection if (resourceManager == null) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs b/src/WebExpress.WebCore.Test/TestRestApiA.cs similarity index 90% rename from src/WebExpress.WebCore.Test/TestRestApiA1X.cs rename to src/WebExpress.WebCore.Test/TestRestApiA.cs index 86386f5..5bb64ae 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1X.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiA.cs @@ -6,19 +6,18 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:resourcea1x.label")] - [Segment("ra1x", "webindex:homepage.label")] + [Title("webindex:apia.label")] + [Segment("apia", "webindex:homepage.label")] [Method(CrudMethod.POST)] [Method(CrudMethod.GET)] [Version(1)] - [Module] - public sealed class TestRestApiA1X : IRestApi + public sealed class TestRestApiA : IRestApi { /// /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The context of the restapi resource. - public TestRestApiA1X(IRestApiContext restApiContext) + public TestRestApiA(IRestApiContext restApiContext) { // test the injection if (restApiContext == null) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs b/src/WebExpress.WebCore.Test/TestRestApiB.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestRestApiA1Y.cs rename to src/WebExpress.WebCore.Test/TestRestApiB.cs index b0cc094..3a3429e 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Y.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiB.cs @@ -6,19 +6,18 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:resourcea1x.label")] - [Segment("ra1y", "webindex:homepage.label")] - [Parent] + [Title("webindex:apib.label")] + [Segment("apib", "webindex:homepage.label")] + [Parent] [Method(CrudMethod.GET)] [Version(2)] - [Module] - public sealed class TestRestApiA1Y : IRestApi + public sealed class TestRestApiB : IRestApi { /// /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The context of the restapi resource. - public TestRestApiA1Y(IRestApiContext restApiContext) + public TestRestApiB(IRestApiContext restApiContext) { // test the injection if (restApiContext == null) diff --git a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs b/src/WebExpress.WebCore.Test/TestRestApiC.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestRestApiA1Z.cs rename to src/WebExpress.WebCore.Test/TestRestApiC.cs index 270c367..553e721 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA1Z.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiC.cs @@ -7,20 +7,19 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// - [Title("webindex:resourcea1x.label")] - [Segment("ra1z", "webindex:homepage.label")] - [Parent] + [Title("webindex:apic.label")] + [Segment("apic", "webindex:homepage.label")] + [Parent] [Method(CrudMethod.GET)] [Version(3)] - [Module] - public sealed class TestRestApiA1Z : RestApi + public sealed class TestRestApiC : RestApi { /// /// Initialization of the rest api resource. Here, for example, managed resources can be loaded. /// /// The component hub. /// The context of the restapi resource. - public TestRestApiA1Z(IComponentHub componentHub, IRestApiContext restApiContext) + public TestRestApiC(IComponentHub componentHub, IRestApiContext restApiContext) : base(restApiContext) { // test the injection diff --git a/src/WebExpress.WebCore.Test/TestStatusPageB404.cs b/src/WebExpress.WebCore.Test/TestStatusPage301.cs similarity index 86% rename from src/WebExpress.WebCore.Test/TestStatusPageB404.cs rename to src/WebExpress.WebCore.Test/TestStatusPage301.cs index c640419..537cf8e 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPageB404.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage301.cs @@ -9,16 +9,15 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:homepage.label")] - [Application()] - [StatusResponse()] + [StatusResponse()] // [Icon("/webexpress/icon.png")] test empty icon - public sealed class TestStatusPageB404 : IStatusPage + public sealed class TestStatusPage301 : IStatusPage { /// /// Initialization of the status page. Here, for example, managed resources can be loaded. /// /// The context of the status page. - private TestStatusPageB404(IStatusPageContext statusPageContext) + private TestStatusPage301(IStatusPageContext statusPageContext) { // test the injection if (statusPageContext == null) diff --git a/src/WebExpress.WebCore.Test/TestStatusPageA400.cs b/src/WebExpress.WebCore.Test/TestStatusPage400.cs similarity index 90% rename from src/WebExpress.WebCore.Test/TestStatusPageA400.cs rename to src/WebExpress.WebCore.Test/TestStatusPage400.cs index d88b1ee..2105240 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPageA400.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage400.cs @@ -10,10 +10,9 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:homepage.label")] - [Application()] [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPageA400 : IStatusPage + public sealed class TestStatusPage400 : IStatusPage { /// /// Returns or sets the status message. @@ -25,7 +24,7 @@ public sealed class TestStatusPageA400 : IStatusPage /// /// The context of the status page. /// The status message. - private TestStatusPageA400(IStatusPageContext statusPageContext, StatusMessage message) + private TestStatusPage400(IStatusPageContext statusPageContext, StatusMessage message) { // test the injection if (statusPageContext == null) diff --git a/src/WebExpress.WebCore.Test/TestStatusPageA404.cs b/src/WebExpress.WebCore.Test/TestStatusPage404.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestStatusPageA404.cs rename to src/WebExpress.WebCore.Test/TestStatusPage404.cs index 38adb89..4ad62d7 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPageA404.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage404.cs @@ -9,16 +9,15 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:homepage.label")] - [Application()] [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPageA404 : IStatusPage + public sealed class TestStatusPage404 : IStatusPage { /// /// Initialization of the status page. Here, for example, managed resources can be loaded. /// /// The context of the status page. - private TestStatusPageA404(IStatusPageContext statusPageContext) + private TestStatusPage404(IStatusPageContext statusPageContext) { // test the injection if (statusPageContext == null) diff --git a/src/WebExpress.WebCore.Test/TestStatusPageB500.cs b/src/WebExpress.WebCore.Test/TestStatusPage500.cs similarity index 64% rename from src/WebExpress.WebCore.Test/TestStatusPageB500.cs rename to src/WebExpress.WebCore.Test/TestStatusPage500.cs index 24857d9..9a7360b 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPageB500.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage500.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebStatusPage; @@ -9,22 +10,35 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. ///
[Title("webindex:homepage.label")] - //[Application()] test for empty application - [StatusResponse()] + [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPageB500 : IStatusPage + public sealed class TestStatusPage500 : IStatusPage { + /// + /// Returns or sets the status message. + /// + public string StatusMessage { get; private set; } + /// /// Initialization of the status page. Here, for example, managed resources can be loaded. /// /// The context of the status page. - private TestStatusPageB500(IStatusPageContext statusPageContext) + /// The status message. + private TestStatusPage500(IStatusPageContext statusPageContext, StatusMessage message) { // test the injection if (statusPageContext == null) { throw new ArgumentNullException(nameof(statusPageContext), "Parameter cannot be null or empty."); } + + // test the injection + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Parameter cannot be null or empty."); + } + + StatusMessage = message?.Message; } /// @@ -38,6 +52,8 @@ public void Process(IRenderContext context) { throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); } + + context.VisualTree.Content = new HtmlText(StatusMessage); } /// diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs index 0644eb1..38068af 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs @@ -111,8 +111,7 @@ public void Test_4() resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, extendetSegments); resourceUri.ServerRoot = new UriResource("http://user@example.com:80"); resourceUri.ApplicationRoot = new UriResource("http://user@example.com:80"); - resourceUri.ModuleRoot = new UriResource("http://user@example.com:80"); - resourceUri.ResourceRoot = new UriResource("http://user@example.com:80/abc"); + resourceUri.EndpointRoot = new UriResource("http://user@example.com:80/abc"); Assert.True ( diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs index 9516b46..748382f 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs @@ -26,8 +26,7 @@ public void ExtendedPath_0() var resourceUri = new UriResource(Uri, segments); resourceUri.ServerRoot = new UriResource("http://user@example.com:80"); resourceUri.ApplicationRoot = new UriResource("http://user@example.com:80"); - resourceUri.ModuleRoot = new UriResource("http://user@example.com:80"); - resourceUri.ResourceRoot = new UriResource("http://user@example.com:80/a/b/c"); + resourceUri.EndpointRoot = new UriResource("http://user@example.com:80/a/b/c"); Assert.True ( diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 07cc63f..de1fce8 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -268,9 +268,8 @@ private Response HandleClient(HttpContext context) resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count)?.PathSegments) { ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments), - ApplicationRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ModuleContext?.ApplicationContext?.ContextPath.PathSegments), - ModuleRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ModuleContext?.ContextPath.PathSegments), - ResourceRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) + ApplicationRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ApplicationContext?.ContextPath.PathSegments), + EndpointRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) }; request.Uri = resourceUri; @@ -280,14 +279,9 @@ private Response HandleClient(HttpContext context) // execute resource request.AddParameter(searchResult.Uri.Parameters.Select(x => new Parameter(x.Key, x.Value, ParameterScope.Url))); - if (searchResult.Instance != null) + if (searchResult.EndpointContext != null) { - response = searchResult.Process(request); - - if (searchResult.Instance is IPage) - { - response.Content += $""; - } + response = WebEx.ComponentHub.EndpointManager.HandleRequest(request, searchResult.EndpointContext); if (response is ResponseNotFound) { @@ -468,7 +462,7 @@ private async Task SendResponseAsync(HttpContext context, Response response) ( message, response.Status, - searchResult?.EndpointContext?.ModuleContext?.ApplicationContext, + searchResult?.EndpointContext?.ApplicationContext, request ); } diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 9d7e28f..a4e8270 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -301,5 +301,12 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in { } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 645bc58..5eeb18d 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -71,6 +71,7 @@ pluginmanager.plugin.processing.start=Das Plugin '{0}' wird ausgeführt. pluginmanager.plugin.processing.end=Die Ausführung des Plugin '{0}' wurde beendet. pluginmanager.fulfilleddependencies=Das Plugin '{0}' erfüllt alle Abhängigkeiten. pluginmanager.unfulfilleddependencies=Das Plugin '{0}' erfüllt eine Abhängigkeit zu dem Plugin '{1}' nicht. +pluginmanager.applicationless=Das Plugin '{0}' besitzt keine Angaben zur Anwendung. applicationmanager.initialization=Der Anwendungsmanager wurde initialisiert. applicationmanager.register=Die Anwendung '{0}' wurde erstellt und im Anwendungsmanager registriert. @@ -81,45 +82,30 @@ applicationmanager.application.processing.start=Die Anwendung '{0}' wird ausgef applicationmanager.application.processing.end=Die Ausführung der Anwendung '{0}' wurde beendet. applicationmanager.application.boot.notfound=Das Plugin '{0}' ist nicht bekannt. -modulemanager.initialization=Der Modulmanager wurde initialisiert. -modulemanager.register=Das Modul '{0}' wurde der Anwendung '{1}' zugewiesen und im Modulmanager registriert. -modulemanager.duplicat=Das Modul '{0}' wurde bereits in der Anwendung '{1}' registriert. -modulemanager.applicationless=Das Modul '{0}' besitzt keine Angaben zur Anwendung. -modulemanager.module=Module: '{0}' für die Anwendung(n) '{1}' -modulemanager.module.initialization=Das Modul '{1}' der Anwendung {0} wurde initialisiert. -modulemanager.module.processing.start=Das Modul '{1}' der Anwendung {0} wird ausgeführt. -modulemanager.module.processing.end=Die Ausführung des Modul '{1}' der Anwendung {0} wurde beendet. - resourcemanager.initialization=Der Ressourcenmanager wurde initialisiert. -resourcemanager.register={0} Ressource(n) wurden dem Modul '{1}' zugewiesen. -resourcemanager.modulenotfound=Das Modul '{1}' wurde nicht gefunden, welches in der Ressource '{0}' angegeben wurde. -resourcemanager.moduleless=Die Ressource '{0}' besitzt keine Angaben zum Modul. -resourcemanager.addresource=Die Ressource '{0}' wurde in dem Modul '{1}' registiert. -resourcemanager.addresource.duplicate=Die Ressource '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. -resourcemanager.addresource.error=Die Ressource '{0}' des Moduls '{1}' konnte nicht hinzugefügt werden. -resourcemanager.sitemap=Inhalt der Sitemap für das Modul '{0}': +resourcemanager.register={0} Ressource(n) wurden der Anwendung '{1}' zugewiesen. +resourcemanager.addresource=Die Ressource '{0}' wurde in der Anwendung '{1}' registiert. +resourcemanager.addresource.duplicate=Die Ressource '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. +resourcemanager.addresource.error=Die Ressource '{0}' der Anwendung '{1}' konnte nicht hinzugefügt werden. +resourcemanager.sitemap=Inhalt der Sitemap für die Anwendung '{0}': resourcemanager.wrongtype=Der Type '{0}' implementiert die Schnittstelle '{1}' nicht. -resourcemanager.resource=Ressource: '{0}' für das Modul '{1}' - -pagemanager.initialization=Der Pagemanager wurde initialisiert. -pagemanager.moduleless=Die Seite '{0}' besitzt keine Angaben zum Modul. -pagemanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. -pagemanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. -pagemanager.resource=Seite: '{0}' für das Modul '{1}' - -restapimanager.initialization=Der RestApiManager wurde initialisiert. -restapimanager.moduleless=Die Seite '{0}' besitzt keine Angaben zum Modul. -restapimanager.addresource=Die Seite '{0}' wurde in dem Modul '{1}' registiert. -restapimanager.addresource.duplicate=Die Seite '{0}' des Moduls '{1}' ist bereits hinzugefügt worden. -restapimanager.resource=Seite: '{0}' für das Modul '{1}' +resourcemanager.resource=Ressource: '{0}' für die Anwednung '{1}' + +pagemanager.initialization=Der Pagemanager wurde initialisiert. +pagemanager.addresource=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. +pagemanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. +pagemanager.resource=Seite: '{0}' für die Anwendung '{1}' + +restapimanager.initialization=Der RestApiManager wurde initialisiert. +restapimanager.addresource=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. +restapimanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. +restapimanager.resource=Seite: '{0}' für die Anwendung '{1}' restapimanager.methodnotsupported=Die Methode '{0}' wird nicht unterstützt. statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. -statuspagemanager.applicationless=Die Statusseite '{0}' besitzt keine Angaben zur Anwendung. -statuspagemanager.applicationrich=Die Statusseite '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. statuspagemanager.register=Der Status '{0}' wurde registriert und der Statusseite '{1}' zugewiesen. statuspagemanager.duplicat=Der Status '{0}' wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. -statuspagemanager.statuscode=Ein Statuscode wurde der Ressource '{1}' für die Anwendung '{0}' nicht zugewiesen. +statuspagemanager.statuscodeless=Ein Statuscode wurde der Ressource '{1}' für die Anwendung '{0}' nicht zugewiesen. statuspagemanager.statuspage=Statuscode: '{0}' sitemapmanager.initialization=Der Sitemap-Manager wurde initialisiert. @@ -134,15 +120,11 @@ sitemapmanager.merge.error=Die beiden Sitemaps '{0}' und '{1}' konnten nicht gem sessionmanager.initialization=Der Sessionmanager wurde initialisiert. eventmanager.initialization=Der Eventmanager wurde initialisiert. -eventmanager.applicationless=Der Eventhandler '{0}' besitzt keine Angaben zur Anwendung. -eventmanager.applicationrich=Der Eventhandler '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. eventmanager.register=Der Eventhandler '{0}' wurde der Anwendung '{1}' zugewiesen und im Eventmanager registriert. eventmanager.duplicate=Der Eventhandler '{0}' wurde bereits in der Anwendung '{1}' registriert. eventmanager.eventless=Der Eventhandler '{0}' besitzt keine Angaben zu einem Event. jobmanager.initialization=Der Jobmanager wurde initialisiert. -jobmanager.applicationless=Der Job '{0}' besitzt keine Angaben zur Anwendung. -jobmanager.applicationrich=Der Job '{0}' besitzt zu viele Anwendungen. Es wird die erste Anwendung verwendet. jobmanager.register=Der Job '{0}' wurde der Anwendung '{1}' zugewiesen und im Jobmanager registriert. jobmanager.duplicate=Der Job '{0}' wurde bereits in der Anwendung '{1}' registriert. jobmanager.jobless=Der Job '{0}' besitzt keine Angaben zu einem Job. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 2860a9b..8d3b49e 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -71,6 +71,7 @@ pluginmanager.plugin.processing.start=The plugin '{0}' is running. pluginmanager.plugin.processing.end=The running of the plugin '{0}' has been stopped. pluginmanager.fulfilleddependencies=The plugin '{0}' fulfills all dependencies. pluginmanager.unfulfilleddependencies=The plugin '{0}' does not fulfill a dependency on the plugin '{1}'. +pluginmanager.applicationless=The plugin '{0}' does not have any information about an application. applicationmanager.initialization=The application manager has been initialized. applicationmanager.registerapplication=The application '{0}' has been created and registered in the application manager. @@ -81,45 +82,30 @@ applicationmanager.application.processing.start=The application '{0}' is running applicationmanager.application.processing.end=The running of the application '{0}' has been stopped. applicationmanager.application.boot.notfound=The plugin '{0}' is unknown. -modulemanager.initialization=The module manager has been initialized. -modulemanager.register=The module '{0}' has been assigned to the application '{1}' and registered in the module manager. -modulemanager.duplicate=The module '{0}' has already been registered in the application '{1}'. -modulemanager.applicationless=The module '{0}' does not have any information about the application. -modulemanager.module=Module: '{0}' for application(s) '{1}' -modulemanager.module.initialization=The module '{1}' of the application '{0}' has been initialized. -modulemanager.module.processing.start=The module '{1}' of the application '{0}' is running. -modulemanager.module.processing.end=The running of the module '{1}' of the application '{0}' has been stopped. - resourcemanager.initialization=The resource manager has been initialized. -resourcemanager.register={0} resource(s) have been assigned to the module '{1}'. -resourcemanager.modulenotfound=The module '{1}' could not be found, which was specified in the resource '{0}'. -resourcemanager.moduleless=The resource '{0}' does not have any information about the module. -resourcemanager.addresource=The resource '{0}' has been registered in the module '{1}'. -resourcemanager.addresource.duplicate=The resource '{0}' of module '{1}' has already been added. -resourcemanager.addresource.error=The resource '{0}' of the module '{1}' could not be added. +resourcemanager.register={0} resource(s) have been assigned to the application '{1}'. +resourcemanager.addresource=The resource '{0}' has been registered in the application '{1}'. +resourcemanager.addresource.duplicate=The resource '{0}' of application '{1}' has already been added. +resourcemanager.addresource.error=The resource '{0}' of the application '{1}' could not be added. resourcemanager.sitemap=Sitemap content resource '{0}' application: resourcemanager.wrongtype=The type '{0}' does not implement the interface '{1}'. -resourcemanager.resource=Resource: '{0}' for module '{1}' +resourcemanager.resource=Resource: '{0}' for application '{1}' pagemanager.initialization=The page manager has been initialized. -pagemanager.moduleless=The page '{0}' does not have any information about the module. -pagemanager.addresource=The page '{0}' has been registered in the module '{1}'. -pagemanager.addresource.duplicate=The page '{0}' of module '{1}' has already been added. -pagemanager.resource=Page: '{0}' for module '{1}' +pagemanager.addresource=The page '{0}' has been registered in the application '{1}'. +pagemanager.addresource.duplicate=The page '{0}' of application '{1}' has already been added. +pagemanager.resource=Page: '{0}' for application '{1}' restapimanager.initialization=The REST API manager has been initialized. -restapimanager.moduleless=The REST API '{0}' does not have any information about the module. -restapimanager.addresource=The REST API '{0}' has been registered in the module '{1}'. -restapimanager.addresource.duplicate=The REST API '{0}' of module '{1}' has already been added. -restapimanager.resource=REST API: '{0}' for module '{1}' +restapimanager.addresource=The REST API '{0}' has been registered in the application '{1}'. +restapimanager.addresource.duplicate=The REST API '{0}' of application '{1}' has already been added. +restapimanager.resource=REST API: '{0}' for application '{1}' restapimanager.methodnotsupported=The method '{0}' is not supported. statuspagemanager.initialization=The status page manager has been initialized. -statuspagemanager.applicationless=The status page '{0}' does not have any information about the application. -statuspagemanager.applicationrich=The status page '{0}' has too many applications. The first application is used. statuspagemanager.register=The status '{0}' has been registered and assigned to the status page '{1}'. statuspagemanager.duplicat=The status '{0}' has already been registered. Therefore, the status page '{1}' is not used. -statuspagemanager.statuscode=A status code has not been assigned to the resource '{1}' for the application '{0}'. +statuspagemanager.statuscodeless=A status code has not been assigned to the resource '{1}' for the application '{0}'. statuspagemanager.resource=Status code: '{0}' sitemapmanager.initialization=The sitemap manager has been initialized. @@ -134,19 +120,15 @@ sitemapmanager.merge.error=The two sitemaps '{0}' and '{1}' could not be merged. sessionmanager.initialization=The session manager has been initialized. eventmanager.initialization=The event manager has been initialized. -eventmanager.applicationless=The event handler '{0}' does not have any information about an application. -eventmanager.applicationrich=The event handler '{0}' has too many applications. The first application is used. eventmanager.register=The event handler '{0}' has been registered in the application '{1}'. eventmanager.duplicate=The event handler '{0}' has already been registered. Therefore, the application '{1}' is not used. eventmanager.eventless=The event handler '{0}' does not have any information about the event. jobmanager.initialization=The schedule manager has been initialized. -eventmanager.applicationless=The job '{0}' does not have any information about an application. -eventmanager.applicationrich=The job '{0}' has too many applications. The first application is used. -eventmanager.register=The job '{0}' has been registered in the application '{1}'. -eventmanager.duplicate=The job '{0}' has already been registered. Therefore, the application '{1}' is not used. -eventmanager.jobless=The job '{0}' does not have any information about the job. -jobmanager.job=Job: '{0}' for module '{1}' +jobmanager.register=The job '{0}' has been registered in the application '{1}'. +jobmanager.duplicate=The job '{0}' has already been registered. Therefore, the application '{1}' is not used. +jobmanager.jobless=The job '{0}' does not have any information about the job. +jobmanager.job=Job: '{0}' for plugin '{1}' jobmanager.job.process=The job '{0}' is executed. jobmanager.cron.parseerror=Syntax error in the timing of a job. The value '{0}' cannot be processed. jobmanager.cron.range=Syntax error in the timing of a job. The value '{0}' is outside the valid range. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs index 6e6f2fc..e151e0c 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebApplication @@ -26,11 +25,6 @@ public class ApplicationContext : IApplicationContext /// public string Description { get; internal set; } - /// - /// Returns an enumeration of options. Options enable optional resources. - /// - public IEnumerable Options { get; internal set; } - /// /// Returns the asset directory. This is mounted in the asset directory of the server. /// diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index d00ffc4..6dbf2bf 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -16,9 +16,9 @@ namespace WebExpress.WebCore.WebApplication /// /// Management of WebExpress applications. /// - public sealed class ApplicationManager : IApplicationManager, IComponentManagerPlugin, IExecutableElements, ISystemComponent + public sealed class ApplicationManager : IApplicationManager, IExecutableElements, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly ApplicationDictionary _dictionary = []; @@ -40,23 +40,16 @@ public sealed class ApplicationManager : IApplicationManager, IComponentManagerP /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private ApplicationManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private ApplicationManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentHub; - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; - _httpServerContext = _componentManager.HttpServerContext; + _httpServerContext = _componentHub.HttpServerContext; _httpServerContext.Log.Debug ( @@ -68,7 +61,7 @@ private ApplicationManager(IComponentHub componentManager, IHttpServerContext ht /// Discovers and registers applications from the specified plugin. /// /// A context of a plugin whose applications are to be registered. - public void Register(IPluginContext pluginContext) + private void Register(IPluginContext pluginContext) { // the plugin has already been registered if (_dictionary.ContainsKey(pluginContext)) @@ -76,7 +69,7 @@ public void Register(IPluginContext pluginContext) return; } - _dictionary.Add(pluginContext, new Dictionary()); + _dictionary.Add(pluginContext, []); var assembly = pluginContext.Assembly; var pluginDict = _dictionary[pluginContext]; @@ -96,7 +89,6 @@ public void Register(IPluginContext pluginContext) var contextPath = string.Empty; var assetPath = "/"; var dataPath = "/"; - var options = new List(); // determining attributes foreach (var customAttribute in type.CustomAttributes @@ -126,23 +118,6 @@ public void Register(IPluginContext pluginContext) { dataPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } - else if (customAttribute.AttributeType == typeof(OptionAttribute)) - { - var value = customAttribute.ConstructorArguments.FirstOrDefault().Value.ToString().ToLower().Trim(); - options.Add(value); - } - else if (customAttribute.AttributeType.Name == typeof(WebExOptionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(WebExOptionAttribute<>).Namespace) - { - var value = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - options.Add(value); - } - else if (customAttribute.AttributeType.Name == typeof(WebExOptionAttribute<,>).Name && customAttribute.AttributeType.Namespace == typeof(WebExOptionAttribute<,>).Namespace) - { - var firstValue = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - var secoundValue = customAttribute.AttributeType.GenericTypeArguments.LastOrDefault()?.FullName?.ToLower(); - - options.Add($"{firstValue}.{secoundValue}"); - } } // creating application context @@ -152,7 +127,6 @@ public void Register(IPluginContext pluginContext) ApplicationId = id, ApplicationName = name, Description = description, - Options = options, AssetPath = Path.Combine(_httpServerContext.AssetPath, assetPath), DataPath = Path.Combine(_httpServerContext.DataPath, dataPath), Icon = UriResource.Combine(_httpServerContext.ContextPath, contextPath, icon), @@ -164,7 +138,7 @@ public void Register(IPluginContext pluginContext) ( type, applicationContext, - _componentManager + _componentHub ); if (!pluginDict.ContainsKey(id)) @@ -195,15 +169,27 @@ public void Register(IPluginContext pluginContext) } /// - /// Discovers and registers applications from the specified plugin. + /// Removes all applications associated with the specified plugin context. /// - /// A list with plugin contexts that contain the applications. - public void Register(IEnumerable pluginContexts) + /// The context of the plugin that contains the applications to remove. + internal void Remove(IPluginContext pluginContext) { - foreach (var pluginContext in pluginContexts) + if (pluginContext == null) + { + return; + } + + if (!_dictionary.ContainsKey(pluginContext)) { - Register(pluginContext); + return; } + + foreach (var applicationContext in _dictionary[pluginContext]) + { + OnRemoveApplication(applicationContext.Value.ApplicationContext); + } + + _dictionary.Remove(pluginContext); } /// @@ -228,27 +214,6 @@ public IApplicationContext GetApplication(string applicationId) return null; } - /// - /// Determines the application contexts for a given application id. - /// - /// The application type. - /// The context of the application or null. - public IApplicationContext GetApplication(Type application) - { - if (application == null) return null; - - var items = _dictionary.Values.SelectMany(x => x.Values) - .Where(x => x.ApplicationClass.Equals(application)) - .FirstOrDefault(); - - if (items != null) - { - return items.ApplicationContext; - } - - return null; - } - /// /// Determines the application contexts for a given application id. /// @@ -256,7 +221,7 @@ public IApplicationContext GetApplication(Type application) /// The context of the application or null. public IApplicationContext GetApplication() { - return GetApplication(typeof(T)); + return GetApplications(typeof(T)).FirstOrDefault(); } /// @@ -306,6 +271,22 @@ public IEnumerable GetApplications(IPluginContext pluginCon return _dictionary[pluginContext].Values.Select(x => x.ApplicationContext); } + /// + /// Determines the application contexts for a given application type. + /// + /// The application type. + /// The contexts of the applications as an enumeration. + public IEnumerable GetApplications(Type application) + { + if (application == null) return null; + + var items = _dictionary.Values.SelectMany(x => x.Values) + .Where(x => x.ApplicationClass.Equals(application) || application.IsAssignableFrom(x.ApplicationClass)) + .Select(x => x.ApplicationContext); + + return items; + } + /// /// Boots the applications. /// @@ -374,30 +355,6 @@ public void ShutDown(IPluginContext pluginContext) } } - /// - /// Removes all applications associated with the specified plugin context. - /// - /// The context of the plugin that contains the applications to remove. - public void Remove(IPluginContext pluginContext) - { - if (pluginContext == null) - { - return; - } - - if (!_dictionary.ContainsKey(pluginContext)) - { - return; - } - - foreach (var applicationContext in _dictionary[pluginContext]) - { - OnRemoveApplication(applicationContext.Value.ApplicationContext); - } - - _dictionary.Remove(pluginContext); - } - /// /// Raises the AddApplication event. /// @@ -416,6 +373,26 @@ private void OnRemoveApplication(IApplicationContext applicationContext) RemoveApplication?.Invoke(this, applicationContext); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + /// /// Information about the component is collected and prepared for output in the log. /// @@ -433,5 +410,14 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ); } } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + } } } diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs index 02e4e67..4cddfc5 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -30,11 +29,6 @@ public interface IApplicationContext : IContext /// string Description { get; } - /// - /// Returns an enumeration of options. Options enable optional resources. - /// - IEnumerable Options { get; } - /// /// Returns the asset directory. This is mounted in the asset directory of the server. /// diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs index 760940e..99fe6e2 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -32,13 +32,6 @@ public interface IApplicationManager : IComponentManager /// The context of the application or null. IApplicationContext GetApplication(string applicationId); - /// - /// Determines the application contexts for a given application id. - /// - /// The application type. - /// The context of the application or null. - IApplicationContext GetApplication(Type application); - /// /// Determines the application contexts for a given application id. /// @@ -59,5 +52,12 @@ public interface IApplicationManager : IComponentManager /// The context of the plugin. /// The contexts of the applications as an enumeration. IEnumerable GetApplications(IPluginContext pluginContext); + + /// + /// Determines the application contexts for a given application type. + /// + /// The application type. + /// The contexts of the applications as an enumeration. + IEnumerable GetApplications(Type application); } } diff --git a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs index 991b257..5c67ab7 100644 --- a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs @@ -3,28 +3,12 @@ namespace WebExpress.WebCore.WebAttribute { - /// - /// Application assignment attribute of the application ID. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute, IEventAttribute - { - /// - /// Initializes a new instance of the class. - /// - /// A specific ApplicationId, regular expression, or * for any application. - public ApplicationAttribute(string applicationId) - { - - } - } - /// /// An application expression attribute, which is determined by the type. /// /// The type of the application. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IModuleAttribute, IStatusPageAttribute, IEventAttribute where T : class, IApplication + public class ApplicationAttribute : Attribute, IPluginAttribute where T : class, IApplication { } diff --git a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs index 78d68a5..78004ba 100644 --- a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class AssetPathAttribute : System.Attribute, IApplicationAttribute, IModuleAttribute + public class AssetPathAttribute : System.Attribute, IApplicationAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs index 874e9b6..4def795 100644 --- a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebAttribute { [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] - public class ContextPathAttribute : Attribute, IApplicationAttribute, IModuleAttribute, IEndpointAttribute + public class ContextPathAttribute : Attribute, IApplicationAttribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs index ab102a5..22af871 100644 --- a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class DataPathAttribute : System.Attribute, IApplicationAttribute, IModuleAttribute + public class DataPathAttribute : System.Attribute, IApplicationAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs index b727de5..2d6d49c 100644 --- a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class DescriptionAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute + public class DescriptionAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IModuleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IModuleAttribute.cs deleted file mode 100644 index 4ecba85..0000000 --- a/src/WebExpress.WebCore/WebAttribute/IModuleAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Interface of a module assignment attribute. - /// - public interface IModuleAttribute - { - } -} diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 76ea0b6..6317e82 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IStatusPageAttribute + public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs index 48703e0..d42542a 100644 --- a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs @@ -3,7 +3,7 @@ /// /// The unique identification key. /// - public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute, IEndpointAttribute + public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs deleted file mode 100644 index 6486b2b..0000000 --- a/src/WebExpress.WebCore/WebAttribute/ModuleAttribute.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using WebExpress.WebCore.WebModule; - -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Specifying a module. - /// - /// The type of the module. - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ModuleAttribute : Attribute, IEndpointAttribute, IModuleAttribute where T : class, IModule - { - /// - /// Initializes a new instance of the class. - /// - public ModuleAttribute() - { - - } - } -} diff --git a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs index d13c48b..da958a4 100644 --- a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class NameAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IModuleAttribute + public class NameAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs deleted file mode 100644 index 2a9ba99..0000000 --- a/src/WebExpress.WebCore/WebAttribute/OptionAttribute.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebResource; - -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Activation of options (e.g. 'webexpress.webapp.settinglog' or 'webexpress.webapp.*'). - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class OptionAttribute : Attribute, IApplicationAttribute - { - /// - /// Initializes a new instance of the class. - /// - /// The option to activate. - public OptionAttribute(string option) - { - - } - } - - /// - /// Activation of options (e.g. 'webexpress.webapp.settinglog' or 'webexpress.webapp.*'). - /// - /// The module or resource class. - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class WebExOptionAttribute : Attribute, IApplicationAttribute where M : class, IModule - { - - } - - /// - /// Activation of options (e.g. 'webexpress.webapp.settinglog' or 'webexpress.webapp.*'). - /// The module or resource class. - /// The resource or resource class. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class WebExOptionAttribute : Attribute, IApplicationAttribute where M : class, IModule where R : class, IResource - { - } -} diff --git a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs deleted file mode 100644 index f43dc58..0000000 --- a/src/WebExpress.WebCore/WebAttribute/OptionalAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Marks a ressorce as optional. This becomes active when the option is specified in the application. - /// - [AttributeUsage(AttributeTargets.Class)] - public class OptionalAttribute : Attribute, IEndpointAttribute - { - /// - /// Initializes a new instance of the class. - /// - public OptionalAttribute() - { - - } - } -} diff --git a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs index 17faf26..434d12e 100644 --- a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs @@ -1,5 +1,5 @@ using System; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebAttribute { diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs index 7a04fad..9e3bf3b 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs @@ -32,18 +32,6 @@ public SegmentDoubleAttribute(string variableName, string display) /// The path segment. public IUriPathSegment ToPathSegment() { - //var expression = @"^[+-]?(\d*,\d+|\d+(,\d*)?)( +[eE][+-]?\d+)?$"; - - //var callBackDisplay = new Func((segment, moduleId, culture) => - //{ - // return null; - //}); - - //var callBackValiables = new Func>(segment => - //{ - // return null; - //}); - return new UriPathSegmentVariableDouble(VariableName, Display); } } diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs index 9a3d8b7..e3440d7 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs @@ -32,18 +32,6 @@ public SegmentStringAttribute(string variableName, string display) /// The path segment. public IUriPathSegment ToPathSegment() { - //var expression = "^[^\"]*$"; - - //var callBackDisplay = new Func((segment, moduleId, culture) => - //{ - // return null; - //}); - - //var callBackValiables = new Func>(segment => - //{ - // return null; - //}); - return new UriPathSegmentVariableString(VariableName, Display); } } diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs index c8aab42..a6b2292 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs @@ -32,23 +32,6 @@ public SegmentUIntAttribute(string variableName, string display) /// The path segment. public IUriPathSegment ToPathSegment() { - //var expression = @"^\d$"; - - //var callBackDisplay = new Func((segment, moduleId, culture) => - //{ - // return Display; - //}); - - //var callBackValiables = new Func>(segment => - //{ - // var dict = new Dictionary - // { - // { VariableName, segment } - // }; - - // return dict; - //}); - return new UriPathSegmentVariableUInt(VariableName, Display); } } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 87e2000..96807f9 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -4,10 +4,10 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent.Model; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPackage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -29,7 +29,7 @@ public class ComponentHub : IComponentHub private readonly InternationalizationManager _internationalizationManager; private readonly PluginManager _pluginManager; private readonly ApplicationManager _applicationManager; - private readonly ModuleManager _moduleManager; + private readonly EndpointManager _endpointManager; private readonly ResourceManager _resourceManager; private readonly PageManager _pageManager; private readonly RestApiManager _restApiManager; @@ -63,7 +63,7 @@ public class ComponentHub : IComponentHub PackageManager, _pluginManager, _applicationManager, - _moduleManager, + _endpointManager, _sitemapManager, _resourceManager, _pageManager, @@ -100,12 +100,6 @@ public class ComponentHub : IComponentHub /// The instance of the application manager. public IApplicationManager ApplicationManager => _applicationManager; - /// - /// Returns the module manager. - /// - /// The instance of the module manager. - public IModuleManager ModuleManager => _moduleManager; - /// /// Returns the event manager. /// @@ -118,6 +112,12 @@ public class ComponentHub : IComponentHub /// The instance of the job manager. public IJobManager JobManager => _jobManager; + /// + /// Returns the endpoint manager. + /// + /// The instance of the endpoint manager. + public IEndpointManager EndpointManager => _endpointManager; + /// /// Returns the resource manager. /// @@ -180,8 +180,8 @@ internal ComponentHub(IHttpServerContext httpServerContext) _pluginManager = CreateInstance(typeof(PluginManager)) as PluginManager; _internationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; - _moduleManager = CreateInstance(typeof(ModuleManager)) as ModuleManager; _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; + _endpointManager = CreateInstance(typeof(EndpointManager)) as EndpointManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; _pageManager = CreateInstance(typeof(PageManager)) as PageManager; _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; @@ -345,7 +345,6 @@ internal void BootComponent(IPluginContext pluginContext) { _pluginManager.Boot(pluginContext); _applicationManager.Boot(pluginContext); - _moduleManager.Boot(pluginContext); foreach (var component in _dictionary.Values .Where(x => x is IExecutableElements) @@ -400,7 +399,6 @@ internal void ShutDownComponent(IPluginContext pluginContext) { _pluginManager.ShutDown(pluginContext); _applicationManager.ShutDown(pluginContext); - _moduleManager.ShutDown(pluginContext); foreach (var component in _dictionary.Values .Where(x => x is IExecutableElements) @@ -480,7 +478,6 @@ internal void LogStatus() ); _applicationManager.PrepareForLog(pluginContext, output, 4); - _moduleManager.PrepareForLog(pluginContext, output, 4); _resourceManager.PrepareForLog(pluginContext, output, 4); _statusPageManager.PrepareForLog(pluginContext, output, 4); _jobManager.PrepareForLog(pluginContext, output, 4); @@ -502,5 +499,12 @@ internal void LogStatus() HttpServerContext.Log.Info(string.Join(Environment.NewLine, output)); } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 8e1b04a..8b2ca2d 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -2,10 +2,10 @@ using System.Collections.Generic; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPackage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -66,12 +66,6 @@ public interface IComponentHub : IComponentManager /// The instance of the application manager. IApplicationManager ApplicationManager { get; } - /// - /// Returns the module manager. - /// - /// The instance of the module manager. - IModuleManager ModuleManager { get; } - /// /// Returns the event manager. /// @@ -84,6 +78,12 @@ public interface IComponentHub : IComponentManager /// The instance of the job manager. IJobManager JobManager { get; } + /// + /// Returns the endpoint manager. + /// + /// The instance of the endpoint manager. + IEndpointManager EndpointManager { get; } + /// /// Returns the resource manager. /// diff --git a/src/WebExpress.WebCore/WebComponent/IComponentManager.cs b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs index be1d1e2..e4555e5 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentManager.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentManager.cs @@ -1,9 +1,11 @@ -namespace WebExpress.WebCore.WebComponent +using System; + +namespace WebExpress.WebCore.WebComponent { /// /// Interface of the manager classes. /// - public interface IComponentManager + public interface IComponentManager : IDisposable { } } diff --git a/src/WebExpress.WebCore/WebComponent/IEndpointManager.cs b/src/WebExpress.WebCore/WebComponent/IEndpointManager.cs deleted file mode 100644 index 2118e1e..0000000 --- a/src/WebExpress.WebCore/WebComponent/IEndpointManager.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace WebExpress.WebCore.WebComponent -{ - /// - /// Represents a endpoint manager. - /// - public interface IEndpointManager : IComponentManager - { - - } -} diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs new file mode 100644 index 0000000..aeae425 --- /dev/null +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebEndpoint +{ + /// + /// The endpoint manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). + /// + public sealed class EndpointManager : IEndpointManager, ISystemComponent + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly Dictionary _registrations = []; + + /// + /// An event that fires when an resource is added. + /// + public event EventHandler AddResource; + + /// + /// An event that fires when an resource is removed. + /// + public event EventHandler RemoveResource; + + /// + /// Returns all endpoints contexts. + /// + public IEnumerable Endpoints => _registrations.Values.SelectMany(x => x.EndpointsResolver()); + + /// + /// Initializes a new instance of the class. + /// + /// The component manager. + /// The reference to the context of the host. + private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress:endpointmanager.initialization") + ); + } + + /// + /// Registers an endpoint context type. + /// + /// The type of the endpoint context. + /// The registration details containing the callback functions. + public void Register(EndpointRegistration registration) where T : IEndpointContext + { + var type = typeof(T); + if (!_registrations.ContainsKey(type)) + { + _registrations[type] = registration; + } + } + + /// + /// Removes the registration for a specific endpoint context type. + /// + /// The type of the endpoint context. + public void Remove() where T : IEndpointContext + { + var type = typeof(T); + _registrations.Remove(type); + } + + //// + /// Returns an enumeration of endpoint contexts. + /// + /// The endpoint type. + /// The application context. + /// An enumeration of endpoint contexts. + public IEnumerable GetEndpoints(Type endpointType, IApplicationContext applicationContext = null) + { + return _registrations.SelectMany(x => x.Value.EndpointResolver(endpointType, applicationContext)); + } + + ///// + ///// Creates a new instance or if caching is active, a possibly existing instance is returned. + ///// + ///// The endpoint context. + ///// The uri. + ///// The search context. + ///// The created endpoint. + //private IEndpoint CreateEndpoint(IEndpointContext endpointContext, UriResource uri, SearchContext searchContext) + //{ + // if (endpointContext == null) + // { + // return null; + // } + + // var type = endpointContext.GetType(); + + // if (_registrations.TryGetValue(type, out var registration)) + // { + // return registration.Factory(endpointContext, uri, searchContext.Culture); + // } + + // throw new InvalidOperationException($"No factory registered for type {type}"); + //} + + /// + /// Handles a request and returns a response. + /// + /// The request to handle. + /// The context of the endpoint handling the request. + /// The response generated by the endpoint. + public Response HandleRequest(Request request, IEndpointContext endpointContext) + { + var registration = _registrations + .Where(x => x.Key == endpointContext?.GetType()) + .Select(x => x.Value) + .FirstOrDefault(); + + return registration?.HandleRequest(request, endpointContext); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + /// The context of the plugin. + /// A list of log entries. + /// The shaft deep. + public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + { + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs similarity index 50% rename from src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs rename to src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs index 3872831..21e4ba6 100644 --- a/src/WebExpress.WebCore/WebSitemap/EndpointRegistration.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs @@ -1,36 +1,30 @@ using System; using System.Collections.Generic; -using System.Globalization; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebSitemap +namespace WebExpress.WebCore.WebEndpoint { /// /// Contains the registration details for an endpoint, including factory and resolver functions. /// public class EndpointRegistration { - /// - /// Returns or sets the factory function to create a specific endpoint. - /// - public Func Factory { get; set; } - /// /// Returns or sets the context resolver function to resolve the corresponding endpoint contexts. /// - public Func> ContextResolver { get; set; } + public Func> EndpointResolver { get; set; } /// /// Returns or sets the endpoint resolver function to resolve additional endpoint contexts. /// - public Func> EndpointResolver { get; set; } + public Func> EndpointsResolver { get; set; } /// /// Returns or sets the function to handle requests. /// - public Func HandleRequest { get; set; } + public Func HandleRequest { get; set; } + + } } diff --git a/src/WebExpress.WebCore/WebComponent/IEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/IEndpoint.cs similarity index 69% rename from src/WebExpress.WebCore/WebComponent/IEndpoint.cs rename to src/WebExpress.WebCore/WebEndpoint/IEndpoint.cs index 8aa299f..8db24c3 100644 --- a/src/WebExpress.WebCore/WebComponent/IEndpoint.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IEndpoint.cs @@ -1,4 +1,6 @@ -namespace WebExpress.WebCore.WebComponent +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebEndpoint { /// /// Defines the base contract for resources of all kinds, such as REST APIs or Pages. diff --git a/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs similarity index 86% rename from src/WebExpress.WebCore/WebComponent/IEndpointContext.cs rename to src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs index e43af26..1feca8d 100644 --- a/src/WebExpress.WebCore/WebComponent/IEndpointContext.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs @@ -1,10 +1,11 @@ using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebEndpoint { /// /// Represents the context of a endpoint. @@ -22,9 +23,9 @@ public interface IEndpointContext : IContext IPluginContext PluginContext { get; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - IModuleContext ModuleContext { get; } + IApplicationContext ApplicationContext { get; } /// /// Provides the conditions that must be met for the resource to be active. diff --git a/src/WebExpress.WebCore/WebEndpoint/IEndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/IEndpointManager.cs new file mode 100644 index 0000000..bf11c67 --- /dev/null +++ b/src/WebExpress.WebCore/WebEndpoint/IEndpointManager.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebEndpoint +{ + /// + /// Represents a endpoint manager. + /// + public interface IEndpointManager : IComponentManager + { + /// + /// Returns all endpoints contexts. + /// + IEnumerable Endpoints { get; } + + /// + /// Registers an endpoint context type. + /// + /// The type of the endpoint context. + /// The registration details containing the callback functions. + void Register(EndpointRegistration registration) where T : IEndpointContext; + + /// + /// Removes the registration for a specific endpoint context type. + /// + /// The type of the endpoint context. + void Remove() where T : IEndpointContext; + + /// + /// Returns an enumeration of endpoint contexts. + /// + /// The endpoint type. + /// The application context. + /// An enumeration of endpoint contexts. + IEnumerable GetEndpoints(Type endpointType, IApplicationContext applicationContext = null); + + ///// + ///// Creates a new instance or if caching is active, a possibly existing instance is returned. + ///// + ///// The endpoint context. + ///// The uri. + ///// The search context. + ///// The created endpoint. + //IEndpoint CreateEndpoint(IEndpointContext endpointContext, UriResource uri, SearchContext searchContext); + + /// + /// Handles a request and returns a response. + /// + /// The request to handle. + /// The context of the endpoint handling the request. + /// The response generated by the endpoint. + Response HandleRequest(Request request, IEndpointContext endpointContext); + } +} diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 70ee8ba..4b4e2bf 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -13,7 +13,7 @@ namespace WebExpress.WebCore.WebEvent /// /// The event manager. /// - public sealed class EventManager : IEventManager, IComponentManagerPlugin, ISystemComponent + public sealed class EventManager : IEventManager, ISystemComponent { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; @@ -47,15 +47,10 @@ internal EventManager(IComponentHub componentHub, IHttpServerContext httpServerC { _componentHub = componentHub; - _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; _httpServerContext = httpServerContext; @@ -110,10 +105,42 @@ public void RaiseEvent(IApplicationContext applicationContext, object sender, } /// - /// Discovers and registers event handlers from the specified plugin. + /// Discovers and binds jobs to an application. + /// + /// The context of the plugin whose jobs are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds jobs to an application. /// - /// A context of a plugin whose event handlers are to be registered. - public void Register(IPluginContext pluginContext) + /// The context of the application whose jobs are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -129,46 +156,33 @@ public void Register(IPluginContext pluginContext) )) { var id = eventHandlerType.FullName?.ToLower(); - var applicationIds = new List(); var eventType = default(Type); foreach (var customAttribute in eventHandlerType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEventAttribute)))) { - if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) - { - applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } - else if (customAttribute.AttributeType.Name == typeof(EventAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(EventAttribute<>).Namespace) + if (customAttribute.AttributeType.Name == typeof(EventAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(EventAttribute<>).Namespace) { eventType = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } } - if (!applicationIds.Any()) + if (eventType == default) { - // no application specified - _httpServerContext.Log.Warning + _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:eventmanager.applicationless", id) + I18N.Translate + ( + "webexpress:eventmanager.eventless", + id + ) ); break; } - if (applicationIds.Count() > 1) - { - // too many specified applications - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:eventmanager.applicationrich", id) - ); - } - - // assign the module to existing applications. - var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); - - if (eventType != default) + // assign the job to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { var eventHandlerContext = new EventHandlerContext() { @@ -210,29 +224,6 @@ public void Register(IPluginContext pluginContext) ); } } - else - { - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:eventmanager.eventless", - id - ) - ); - } - } - } - - /// - /// Discovers and registers entries from the specified plugin. - /// - /// A list with plugin contexts that contain the jobs. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); } } @@ -240,7 +231,7 @@ public void Register(IEnumerable pluginContexts) /// Removes all jobs associated with the specified plugin context. /// /// The context of the plugin that contains the event to remove. - public void Remove(IPluginContext pluginContext) + internal void Remove(IPluginContext pluginContext) { // the plugin has not been registered in the manager if (_dictionary.TryGetValue(pluginContext, out var value)) @@ -256,6 +247,32 @@ public void Remove(IPluginContext pluginContext) } } + /// + /// Removes all jobs associated with the specified application context. + /// + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + foreach (var pluginDict in _dictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var eventHandlerItem in appDict.Values.SelectMany(x => x)) + { + OnRemoveEventHandler(eventHandlerItem.EventHandlerContext); + eventHandlerItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + } + /// /// Raises the AddEventHandler event. /// @@ -274,6 +291,46 @@ private void OnRemoveEventHandler(IEventHandlerContext eventHandlerContext) RemoveEventHandler?.Invoke(this, eventHandlerContext); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Information about the component is collected and prepared for output in the event. /// @@ -296,5 +353,16 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in // ); //} } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + } } } diff --git a/src/WebExpress.WebCore/WebExpress.WebCore.csproj b/src/WebExpress.WebCore/WebExpress.WebCore.csproj index 57ef2f9..b87036d 100644 --- a/src/WebExpress.WebCore/WebExpress.WebCore.csproj +++ b/src/WebExpress.WebCore/WebExpress.WebCore.csproj @@ -36,6 +36,7 @@ + diff --git a/src/WebExpress.WebCore/WebJob/Cron.cs b/src/WebExpress.WebCore/WebJob/Cron.cs index ba62452..c41dd1d 100644 --- a/src/WebExpress.WebCore/WebJob/Cron.cs +++ b/src/WebExpress.WebCore/WebJob/Cron.cs @@ -17,29 +17,29 @@ public class Cron private static IHttpServerContext Context { get; set; } /// - /// The minute 0-59 or * for any. Comma seperated values or ranges (-) are also possible. + /// Returns the minute 0-59 or * for any. Comma seperated values or ranges (-) are also possible. /// - private List Minute { get; } = new List(); + public IEnumerable Minute { get; private set; } /// - /// The hour 0-23 or * for any. Comma seperated values or ranges (-) are also possible. + /// Returns the hour 0-23 or * for any. Comma seperated values or ranges (-) are also possible. /// - private List Hour { get; } = new List(); + public IEnumerable Hour { get; private set; } /// - /// The day 1-31 or * for any. Comma seperated values or ranges (-) are also possible. + /// Returns the day 1-31 or * for any. Comma seperated values or ranges (-) are also possible. /// - private List Day { get; } = new List(); + public IEnumerable Day { get; private set; } /// - /// The month 1-12 or * for any. Comma seperated values or ranges (-) are also possible. + /// Returns the month 1-12 or * for any. Comma seperated values or ranges (-) are also possible. /// - private List Month { get; } = new List(); + public IEnumerable Month { get; private set; } /// - /// The day of the week 0-6 (Sunday-Saturday) or * for any. Comma seperated values or ranges (-) are also possible. + /// Returns the day of the week 0-6 (Sunday-Saturday) or * for any. Comma seperated values or ranges (-) are also possible. /// - private List Weekday { get; } = new List(); + public IEnumerable Weekday { get; private set; } /// /// Initializes a new instance of the class. @@ -54,11 +54,11 @@ public Cron(IHttpServerContext context, string minute = "*", string hour = "*", { Context = context; - Minute.AddRange(Parse(minute, 0, 60)); - Hour.AddRange(Parse(hour, 0, 24)); - Day.AddRange(Parse(day, 1, 31)); - Month.AddRange(Parse(month, 1, 12)); - Weekday.AddRange(Parse(weekday, 0, 7)); + Minute = Parse(minute, 0, 60); + Hour = Parse(hour, 0, 24); + Day = Parse(day, 1, 31); + Month = Parse(month, 1, 12); + Weekday = Parse(weekday, 0, 7); } /// @@ -68,7 +68,7 @@ public Cron(IHttpServerContext context, string minute = "*", string hour = "*", /// The minimum. /// The maximum. /// The parsed values. - private IEnumerable Parse(string value, int minValue, int maxValue) + private static IEnumerable Parse(string value, int minValue, int maxValue) { var items = new List() as IEnumerable; value = value?.ToLower().Trim(); diff --git a/src/WebExpress.WebCore/WebJob/IJobManager.cs b/src/WebExpress.WebCore/WebJob/IJobManager.cs index 8c65765..78496d5 100644 --- a/src/WebExpress.WebCore/WebJob/IJobManager.cs +++ b/src/WebExpress.WebCore/WebJob/IJobManager.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebJob @@ -12,5 +14,13 @@ public interface IJobManager : IComponentManager /// Returns all jobs contextes. /// IEnumerable Jobs { get; } + + /// + /// Returns a JobContext instance associated with an application. + /// + /// The context of the application. + /// The type of the job. + /// A JobContext instance. + IJobContext GetJob(IApplicationContext applicationContext, Type jobType); } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 648f500..33b8fee 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebJob.Model; @@ -14,7 +15,7 @@ namespace WebExpress.WebCore.WebJob /// /// This class manages the processing of cyclic jobs. It provides methods to register, remove, and execute jobs. /// - public sealed class JobManager : IJobManager, IComponentManagerPlugin, ISystemComponent, IExecutableElements + public sealed class JobManager : IJobManager, ISystemComponent, IExecutableElements { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; @@ -34,7 +35,7 @@ public sealed class JobManager : IJobManager, IComponentManagerPlugin, ISystemCo public event EventHandler RemoveJob; /// - /// Returns all jobs contextes. + /// Returns all job contextes. /// public IEnumerable Jobs => _staticScheduleDictionary .SelectMany(x => x.Value) @@ -48,20 +49,14 @@ public sealed class JobManager : IJobManager, IComponentManagerPlugin, ISystemCo /// /// The component manager. /// The reference to the context of the host. - internal JobManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private JobManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentHub = componentManager; - _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; - + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -74,10 +69,67 @@ internal JobManager(IComponentHub componentManager, IHttpServerContext httpServe } /// - /// Discovers and registers jobs from the specified plugin. + /// Registers a dynamic job. + /// + /// The plugin context. + /// The cropn-object. + /// The job. + private IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob + { + // create context + var jobContext = new JobContext() + { + PluginContext = pluginContext, + JobId = typeof(T).FullName?.ToLower(), + Cron = cron + }; + + var item = new ScheduleItem(_componentHub, pluginContext, null, jobContext, typeof(T)); + + _dynamicScheduleList.Append(item); + + OnAddJob(jobContext); + + return item.Instance; + } + + /// + /// Discovers and binds static jobs to an application. + /// + /// The context of the plugin whose jobs are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_staticScheduleDictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds jobs to an application. + /// + /// The context of the application whose jobs are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_staticScheduleDictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. /// - /// A context of a plugin whose jobs are to be registered. - public void Register(IPluginContext pluginContext) + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -90,7 +142,7 @@ public void Register(IPluginContext pluginContext) )) { var id = job.FullName?.ToLower(); - var applicationIds = new List(); + var minute = "*"; var hour = "*"; var day = "*"; @@ -106,66 +158,50 @@ public void Register(IPluginContext pluginContext) weekday = customAttribute.ConstructorArguments.Skip(4).FirstOrDefault().Value?.ToString(); } - foreach (var customAttribute in job.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEventAttribute)))) - { - if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) - { - applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } - } - - if (!applicationIds.Any()) - { - // no application specified - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:jobmanager.applicationless", id) - ); - - break; - } - - if (applicationIds.Count() > 1) - { - // too many specified applications - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:jobmanager.applicationrich", id) - ); - } - - // assign the module to existing applications. - var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); - - if (job != default) + // assign the job to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { var jobContext = new JobContext() { JobId = job.FullName.ToLower(), PluginContext = pluginContext, ApplicationContext = applicationContext, - Cron = new Cron(pluginContext.Host, minute, hour, day, month, weekday), + Cron = new Cron(_httpServerContext, minute, hour, day, month, weekday), }; - if (_staticScheduleDictionary.AddScheduleItem - ( - pluginContext, - applicationContext, - new ScheduleItem(_componentHub, pluginContext, applicationContext, jobContext, job) - )) + if (job != default) { - OnAddJob(jobContext); - - _httpServerContext.Log.Debug + if (_staticScheduleDictionary.AddScheduleItem ( - I18N.Translate + pluginContext, + applicationContext, + new ScheduleItem(_componentHub, pluginContext, applicationContext, jobContext, job) + )) + { + OnAddJob(jobContext); + + _httpServerContext.Log.Debug ( - "webexpress:jobmanager.register", - id, - applicationContext.ApplicationId - ) - ); + I18N.Translate + ( + "webexpress:jobmanager.register", + id, + applicationContext.ApplicationId + ) + ); + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:jobmanager.duplicate", + id, + applicationContext.ApplicationId + ) + ); + } } else { @@ -173,62 +209,76 @@ public void Register(IPluginContext pluginContext) ( I18N.Translate ( - "webexpress:jobmanager.duplicate", - id, - applicationContext.ApplicationId + "webexpress:jobmanager.jobless", + id ) ); } } - else - { - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:jobmanager.jobless", - id - ) - ); - } } } /// - /// Discovers and registers entries from the specified plugin. + /// Removes all jobs associated with the specified plugin context. /// - /// A list with plugin contexts that contain the jobs. - public void Register(IEnumerable pluginContexts) + /// The context of the plugin that contains the jobs to remove. + internal void Remove(IPluginContext pluginContext) { - foreach (var pluginContext in pluginContexts) + if (pluginContext == null) { - Register(pluginContext); + return; } + + // the plugin has not been registered in the manager + if (!_staticScheduleDictionary.ContainsKey(pluginContext)) + { + return; + } + + foreach (var scheduleItem in _staticScheduleDictionary[pluginContext].Values + .SelectMany(x => x.Values) + .SelectMany(x => x)) + { + OnRemoveJob(scheduleItem.JobContext); + scheduleItem.Dispose(); + } + + _staticScheduleDictionary.Remove(pluginContext); } /// - /// Registers a job. + /// Removes all jobs associated with the specified application context. /// - /// The plugin context. - /// The cropn-object. - /// The job. - public IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) { - // create context - var jobContext = new JobContext() + if (applicationContext == null) { - PluginContext = pluginContext, - JobId = typeof(T).FullName?.ToLower(), - Cron = cron - }; - - var item = new ScheduleItem(_componentHub, pluginContext, null, jobContext, typeof(T)); + return; + } - _dynamicScheduleList.Append(item); + foreach (var pluginDict in _staticScheduleDictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var scheduleItem in appDict.Values.SelectMany(x => x)) + { + OnRemoveJob(scheduleItem.JobContext); + scheduleItem.Dispose(); + } + } - OnAddJob(jobContext); + pluginDict.Remove(applicationContext); + } + } - return item.Instance; + /// + /// Removes a dynamic job. + /// + /// The job to remove. + public void Remove(IJob job) + { + _dynamicScheduleList.RemoveAll(x => x == job); } /// @@ -249,6 +299,46 @@ private void OnRemoveJob(IJobContext jobContext) RemoveJob?.Invoke(this, jobContext); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Executes the schedule. /// @@ -321,47 +411,19 @@ private void Update() } /// - /// Stop running the scheduler. - /// - public void ShutDown() - { - _tokenSource.Cancel(); - } - - /// - /// Removes all jobs associated with the specified plugin context. - /// - /// The context of the plugin that contains the jobs to remove. - public void Remove(IPluginContext pluginContext) - { - if (pluginContext == null) - { - return; - } - - // the plugin has not been registered in the manager - if (!_staticScheduleDictionary.ContainsKey(pluginContext)) - { - return; - } - - foreach (var scheduleItem in _staticScheduleDictionary[pluginContext].Values - .SelectMany(x => x.Values) - .SelectMany(x => x)) - { - scheduleItem.Dispose(); - } - - _staticScheduleDictionary.Remove(pluginContext); - } - - /// - /// Removes a job. + /// Returns a JobContext instance associated with an application. /// - /// The job to remove. - public void Remove(IJob job) + /// The context of the application. + /// The type of the job. + /// A JobContext instance. + public IJobContext GetJob(IApplicationContext applicationContext, Type jobType) { - _dynamicScheduleList.RemoveAll(x => x == job); + return _staticScheduleDictionary + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Union(_dynamicScheduleList.Select(x => x)) + .FirstOrDefault(x => x.JobContext.ApplicationContext == applicationContext && x.JobClass == jobType)?.JobContext; } /// @@ -386,5 +448,18 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in ); } } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + _tokenSource.Cancel(); + } } } diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 0c9855e..49547bf 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -109,5 +109,12 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in I18N.Translate("webexpress:logmanager.titel") ); } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs b/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs new file mode 100644 index 0000000..76ed162 --- /dev/null +++ b/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs @@ -0,0 +1,35 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebStatusPage; + +namespace WebExpress.WebCore.WebMessage +{ + /// + /// Represents a response for a resource moved permanently (301) according to RFC 2616 Section 6. + /// + [StatusCode(301)] + public class ResponseMovedPermanently : Response + { + /// + /// Initializes a new instance of the class. + /// + public ResponseMovedPermanently() + : this(null) + { + + } + + /// + /// Initializes a new instance of the class. + /// + /// The user defined status message or null. + public ResponseMovedPermanently(StatusMessage message) + { + var content = message?.Message ?? "404301 - Moved Permanently"; + Reason = "Moved Permanently"; + + Header.ContentType = "text/html"; + Header.ContentLength = content.Length; + Content = content; + } + } +} diff --git a/src/WebExpress.WebCore/WebModule/IModule.cs b/src/WebExpress.WebCore/WebModule/IModule.cs deleted file mode 100644 index 8639e39..0000000 --- a/src/WebExpress.WebCore/WebModule/IModule.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using WebExpress.WebCore.WebComponent; - -namespace WebExpress.WebCore.WebModule -{ - /// - /// Interface of a module - /// - public interface IModule : IComponent, IDisposable - { - /// - /// Called when the module starts working. The call is concurrent. - /// - void Run(); - } -} diff --git a/src/WebExpress.WebCore/WebModule/IModuleContext.cs b/src/WebExpress.WebCore/WebModule/IModuleContext.cs deleted file mode 100644 index 7e4bda5..0000000 --- a/src/WebExpress.WebCore/WebModule/IModuleContext.cs +++ /dev/null @@ -1,55 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.WebModule -{ - public interface IModuleContext : IContext - { - /// - /// Returns the context of the associated plugin. - /// - IPluginContext PluginContext { get; } - - /// - /// Returns the associated application context. - /// - IApplicationContext ApplicationContext { get; } - - /// - /// Returns the modul id. - /// - string ModuleId { get; } - - /// - /// Returns the module name. - /// - string ModuleName { get; } - - /// - /// Returns the description. - /// - string Description { get; } - - /// - /// Returns the asset directory. - /// - string AssetPath { get; } - - /// - /// Returns the data directory. - /// - string DataPath { get; } - - /// - /// Returns the context path. - /// - UriResource ContextPath { get; } - - /// - /// Returns the icon uri. - /// - UriResource Icon { get; } - } -} diff --git a/src/WebExpress.WebCore/WebModule/IModuleManager.cs b/src/WebExpress.WebCore/WebModule/IModuleManager.cs deleted file mode 100644 index 13841f7..0000000 --- a/src/WebExpress.WebCore/WebModule/IModuleManager.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebModule -{ - /// - /// The interface of the module manager. - /// - public interface IModuleManager : IComponentManager - { - /// - /// An event that fires when an module is added. - /// - event EventHandler AddModule; - - /// - /// An event that fires when an module is removed. - /// - event EventHandler RemoveModule; - - /// - /// Returns all stored modules. - /// - public IEnumerable Modules { get; } - - /// - /// Determines the module for a given application context and module id. - /// - /// The type of the application. - /// The type of the module. - /// The context of the module or null. - public IModuleContext GetModule(Type applicationType, Type moduleType); - - /// - /// Determines the module for a given application context and module id. - /// - /// The context of the application. - /// The modul id. - /// The context of the module or null. - public IModuleContext GetModule(IApplicationContext applicationContext, string moduleId); - - /// - /// Determines the module for a given application context and module id. - /// - /// The context of the application. - /// The module class. - /// The context of the module or null. - public IModuleContext GetModule(IApplicationContext applicationContext, Type moduleClass); - - /// - /// Determines the module for a given plugin context and module id. - /// - /// The context of the plugin. - /// The modul id. - /// An enumeration of the module contexts for the given plugin and module id. - public IEnumerable GetModules(IPluginContext pluginContext, string moduleId); - - /// - /// Determines the module for a given plugin context and module id. - /// - /// The context of the plugin. - /// The modul id. - /// An enumeration of the module contexts for the given plugin and module id. - public IEnumerable GetModules(IApplicationContext applicationContext); - } -} diff --git a/src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs b/src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs deleted file mode 100644 index dc52168..0000000 --- a/src/WebExpress.WebCore/WebModule/Model/ModuleDictionary.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebModule.Model -{ - /// - /// Key = plugin context - /// Value = { Key = module id, Value = module item } - /// - internal class ModuleDictionary : Dictionary> - { - } -} diff --git a/src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs b/src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs deleted file mode 100644 index c0d06b3..0000000 --- a/src/WebExpress.WebCore/WebModule/Model/ModuleItem.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.WebModule.Model -{ - /// - /// Represents a module entry in the module directory. - /// - internal class ModuleItem : IDisposable - { - /// - /// The assembly that contains the module. - /// - public Assembly Assembly { get; internal set; } - - /// - /// Returns the module class. - /// - public Type ModuleClass { get; internal set; } - - /// - /// Returns the context of the associated plugin. - /// - public IPluginContext PluginContext { get; internal set; } - - /// - /// Returns the associated application ids. - /// - public IEnumerable Applications { get; set; } - - /// - /// Returns the modul id. - /// - public string ModuleId { get; internal set; } - - /// - /// Returns the module name. - /// - public string ModuleName { get; internal set; } - - /// - /// Returns the description. - /// - public string Description { get; internal set; } - - /// - /// Returns the asset directory. - /// - public string AssetPath { get; internal set; } - - /// - /// Returns the data directory. - /// - public string DataPath { get; internal set; } - - /// - /// Returns the context path. - /// - public UriResource ContextPath { get; internal set; } - - /// - /// Returns the icon uri. - /// - public UriResource Icon { get; internal set; } - - /// - /// Returns the log to write status messages to the console and to a log file. - /// - public ILog Log { get; internal set; } - - /// - /// Returns the directory where the module instances are listed. - /// - public IDictionary Dictionary { get; } - = new Dictionary(); - - /// - /// An event that fires when an module is added. - /// - public event EventHandler AddModule; - - /// - /// An event that fires when an module is removed. - /// - public event EventHandler RemoveModule; - - /// - /// Adds an application assignment - /// - /// The context of the application. - public void AddApplication(IApplicationContext applicationContext) - { - // only if no instance has been created yet - if (Dictionary.ContainsKey(applicationContext)) - { - return; - } - - // create context - var moduleContext = new ModuleContext() - { - PluginContext = PluginContext, - ApplicationContext = applicationContext, - ModuleId = ModuleId, - ModuleName = ModuleName, - Description = Description, - Icon = UriResource.Combine(applicationContext.ContextPath, ContextPath, Icon), - AssetPath = Path.Combine(applicationContext.AssetPath, AssetPath), - DataPath = Path.Combine(applicationContext.DataPath, DataPath), - ContextPath = UriResource.Combine(applicationContext.ContextPath, ContextPath) - }; - - if - ( - Applications.Contains("*") || - Applications.Contains(applicationContext.ApplicationId?.ToLower()) || - Applications.Where(x => Regex.Match(applicationContext.ApplicationId?.ToLower(), x).Success).Any() - ) - { - Dictionary.Add - ( - applicationContext, - new ModuleItemInstance() - { - ModuleContext = moduleContext - } - ); - - // raises the AddModule event - OnAddModule(moduleContext); - } - } - - /// - /// Remove an application assignment - /// - /// The context of the application. - public void DetachApplication(IApplicationContext applicationContext) - { - // not an instance has been created yet - if (!Dictionary.ContainsKey(applicationContext)) - { - return; - } - - var moduleItemValue = Dictionary[applicationContext]; - OnRemoveModule(moduleItemValue.ModuleContext); - - Dictionary.Remove(applicationContext); - } - - /// - /// Boots the module of each existing application if not yet booted. - /// - public void Boot() - { - foreach (var item in Dictionary.Values.Where(x => x.ModuleInstance == null)) - { - // create module - item.ModuleInstance = Activator.CreateInstance(ModuleClass) as IModule; - - // thread termination. - var token = item.CancellationTokenSource.Token; - - // execute modules concurrently - Task.Run(() => - { - Log.Debug - ( - message: I18N.Translate - ( - "webexpress:modulemanager.module.processing.start", - item.ModuleContext.ApplicationContext.ApplicationId, - item.ModuleContext.PluginContext.PluginId - ) - ); - - item.ModuleInstance.Run(); - - Log.Debug - ( - message: I18N.Translate - ( - "webexpress:modulemanager.module.processing.end", - item.ModuleContext.ApplicationContext.ApplicationId, - item.ModuleContext.PluginContext.PluginId - ) - ); - - token.ThrowIfCancellationRequested(); - }, token); - } - } - - /// - /// Terminate modules of a plugin. - /// - public void ShutDown() - { - foreach (var item in Dictionary.Values) - { - item.CancellationTokenSource?.Cancel(); - } - } - - /// - /// Terminate modules of a application. - /// - /// The context of the application containing the modules. - public void ShutDown(IApplicationContext applicationContext) - { - if (Dictionary.ContainsKey(applicationContext) - && Dictionary[applicationContext].CancellationTokenSource != null) - { - Dictionary[applicationContext].CancellationTokenSource.Cancel(); - } - } - - /// - /// Raises the AddModule event. - /// - private void OnAddModule(IModuleContext moduleContext) - { - AddModule?.Invoke(this, moduleContext); - } - - /// - /// Raises the RemoveModule event. - /// - /// The module context. - private void OnRemoveModule(IModuleContext moduleContext) - { - RemoveModule?.Invoke(this, moduleContext); - } - - /// - /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. - /// - public void Dispose() - { - foreach (Delegate d in AddModule.GetInvocationList()) - { - AddModule -= (EventHandler)d; - } - - foreach (Delegate d in RemoveModule.GetInvocationList()) - { - RemoveModule -= (EventHandler)d; - } - } - - /// - /// Convert the module element to a string. - /// - /// The resource element in its string representation. - public override string ToString() - { - return $"Module '{ModuleId}'"; - } - } -} diff --git a/src/WebExpress.WebCore/WebModule/Module.cs b/src/WebExpress.WebCore/WebModule/Module.cs deleted file mode 100644 index c9f1dce..0000000 --- a/src/WebExpress.WebCore/WebModule/Module.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace WebExpress.WebCore.WebModule -{ - /// - /// This represents an module. - /// - public abstract class Module : IModule - { - /// - /// Returns the context of the module. - /// - public IModuleContext ModuleContext { get; private set; } - - /// - /// Initialization of the module. - /// - /// The context. - public virtual void Initialization(IModuleContext moduleContext) - { - ModuleContext = moduleContext; - } - - /// - /// Called when the application starts working. The call is concurrent. - /// - public virtual void Run() - { - } - - /// - /// Release unmanaged resources that have been reserved during use. - /// - public virtual void Dispose() - { - } - } -} diff --git a/src/WebExpress.WebCore/WebModule/ModuleContext.cs b/src/WebExpress.WebCore/WebModule/ModuleContext.cs deleted file mode 100644 index ff9b4d7..0000000 --- a/src/WebExpress.WebCore/WebModule/ModuleContext.cs +++ /dev/null @@ -1,70 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.WebModule -{ - public class ModuleContext : IModuleContext - { - /// - /// Returns the context of the associated plugin. - /// - public IPluginContext PluginContext { get; internal set; } - - /// - /// Returns the associated application context. - /// - public IApplicationContext ApplicationContext { get; internal set; } - - /// - /// Returns the modul id. - /// - public string ModuleId { get; internal set; } - - /// - /// Returns the module name. - /// - public string ModuleName { get; internal set; } - - /// - /// Returns the description. - /// - public string Description { get; internal set; } - - /// - /// Returns the asset directory. - /// - public string AssetPath { get; internal set; } - - /// - /// Returns the data directory. - /// - public string DataPath { get; internal set; } - - /// - /// Returns the context path. - /// - public UriResource ContextPath { get; internal set; } - - /// - /// Returns the icon uri. - /// - public UriResource Icon { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - internal ModuleContext() - { - } - - /// - /// Conversion of the module context into its string representation. - /// - /// The string that uniquely represents the module. - public override string ToString() - { - return $"{ApplicationContext?.ApplicationId}:{ModuleId}"; - } - } -} diff --git a/src/WebExpress.WebCore/WebModule/ModuleItemInstance.cs b/src/WebExpress.WebCore/WebModule/ModuleItemInstance.cs deleted file mode 100644 index 55f30cc..0000000 --- a/src/WebExpress.WebCore/WebModule/ModuleItemInstance.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading; - -namespace WebExpress.WebCore.WebModule -{ - public class ModuleItemInstance - { - /// - /// Returns the module context. - /// - public IModuleContext ModuleContext { get; internal set; } - - /// - /// Returns the module instance or null if not already created. - /// - public IModule ModuleInstance { get; internal set; } - - /// - /// Returns the cancel token or null if not already created. - /// - public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); - } -} diff --git a/src/WebExpress.WebCore/WebModule/ModuleManager.cs b/src/WebExpress.WebCore/WebModule/ModuleManager.cs deleted file mode 100644 index a5d520e..0000000 --- a/src/WebExpress.WebCore/WebModule/ModuleManager.cs +++ /dev/null @@ -1,463 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebModule.Model; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.WebModule -{ - /// - /// The module manager manages the WebExpress modules. - /// - public sealed class ModuleManager : IModuleManager, IComponentManagerPlugin, IExecutableElements, ISystemComponent - { - private readonly IComponentHub _componentManager; - private readonly IHttpServerContext _httpServerContext; - private readonly ModuleDictionary _dictionary = []; - - /// - /// An event that fires when an module is added. - /// - public event EventHandler AddModule; - - /// - /// An event that fires when an module is removed. - /// - public event EventHandler RemoveModule; - - /// - /// Delivers all stored modules. - /// - public IEnumerable Modules => _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Dictionary.Values) - .Select(x => x.ModuleContext); - - /// - /// Initializes a new instance of the class. - /// - /// The component manager. - /// The reference to the context of the host. - private ModuleManager(IComponentHub componentManager, IHttpServerContext httpServerContext) - { - _componentManager = componentManager; - - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; - - _componentManager.ApplicationManager.AddApplication += (sender, applicationContext) => - { - AssignToApplication(applicationContext); - }; - - _componentManager.ApplicationManager.RemoveApplication += (sender, applicationContext) => - { - DetachFromApplication(applicationContext); - }; - - _httpServerContext = httpServerContext; - - _httpServerContext.Log.Debug - ( - I18N.Translate("webexpress:modulemanager.initialization") - ); - } - - /// - /// Discovers and registers modules from the specified plugin. - /// - /// A context of a plugin whose modules are to be registered. - public void Register(IPluginContext pluginContext) - { - if (_dictionary.ContainsKey(pluginContext)) - { - return; - } - - var assembly = pluginContext.Assembly; - - foreach (var type in assembly.GetExportedTypes().Where - ( - x => x.IsClass && - x.IsSealed && - x.IsPublic && - x.GetInterface(typeof(IModule).Name) != null - )) - { - var id = type.FullName?.ToLower(); - var name = type.Name.ToLower(); - var icon = string.Empty; - var description = string.Empty; - var contextPath = string.Empty; - var assetPath = string.Empty; - var dataPath = string.Empty; - var applicationIds = new List(); - - foreach (var customAttribute in type.CustomAttributes - .Where(x => x.AttributeType.GetInterfaces() - .Contains(typeof(IModuleAttribute)))) - { - if (customAttribute.AttributeType == typeof(NameAttribute)) - { - name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(IconAttribute)) - { - icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(DescriptionAttribute)) - { - description = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) - { - contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(AssetPathAttribute)) - { - assetPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(DataPathAttribute)) - { - dataPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } - else if (customAttribute.AttributeType == typeof(ApplicationAttribute)) - { - applicationIds.Add(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString().Trim()); - } - else if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) - { - applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } - } - - if (!applicationIds.Any()) - { - // no application specified - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:modulemanager.applicationless", id) - ); - } - - _dictionary.TryAdd(pluginContext, new Dictionary()); - var item = _dictionary[pluginContext]; - - if (!item.ContainsKey(id)) - { - var moduleItem = new ModuleItem() - { - Assembly = assembly, - ModuleClass = type, - PluginContext = pluginContext, - Applications = applicationIds, - ModuleId = id, - ModuleName = name, - Description = description, - Icon = new UriResource(icon), - AssetPath = assetPath, - ContextPath = new UriResource(contextPath), - DataPath = dataPath, - Log = _httpServerContext.Log - }; - - moduleItem.AddModule += (s, e) => - { - OnAddModule(e); - }; - - moduleItem.RemoveModule += (s, e) => - { - OnRemoveModule(e); - }; - - item.Add(id, moduleItem); - - // assign the module to existing applications. - foreach (var applicationContext in _componentManager.ApplicationManager.Applications) - { - AssignToApplication(applicationContext); - } - - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:modulemanager.register", - id, - string.Join(", ", applicationIds) - ) - ); - } - else - { - _httpServerContext.Log.Warning - ( - I18N.Translate - ( - "webexpress:modulemanager.duplicate", - id, - string.Join(", ", applicationIds) - ) - ); - } - } - } - - /// - /// Discovers and registers modules from the specified plugin. - /// - /// A list with plugin contexts that contain the modules. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); - } - } - - /// - /// Assign existing modules to the application. - /// - /// The context of the application. - private void AssignToApplication(IApplicationContext applicationContext) - { - foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) - { - if (moduleItem.Applications.Contains("*") - || moduleItem.Applications.Contains(applicationContext?.ApplicationId?.ToLower())) - { - moduleItem.AddApplication(applicationContext); - } - } - } - - /// - /// Remove an existing modules to the application. - /// - /// The context of the application. - private void DetachFromApplication(IApplicationContext applicationContext) - { - foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) - { - if (moduleItem.Applications.Contains("*") - || moduleItem.Applications.Contains(applicationContext?.ApplicationId?.ToLower())) - { - moduleItem.DetachApplication(applicationContext); - } - } - } - - /// - /// Determines the module for a given application context and module id. - /// - /// The type of the application. - /// The type of the module. - /// The context of the module or null. - public IModuleContext GetModule(Type applicationType, Type moduleType) - { - var applicationContext = _componentManager.ApplicationManager.GetApplication(applicationType); - var item = _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.Dictionary.ContainsKey(applicationContext)) - .Where(x => x.ModuleClass.Equals(moduleType)) - .Select(x => x.Dictionary[applicationContext]) - .Select(x => x.ModuleContext) - .FirstOrDefault(); - - return item; - } - - /// - /// Determines the module for a given application context and module id. - /// - /// The context of the application. - /// The modul id. - /// The context of the module or null. - public IModuleContext GetModule(IApplicationContext applicationContext, string moduleId) - { - var item = _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.Dictionary.ContainsKey(applicationContext)) - .Select(x => x.Dictionary[applicationContext]) - .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.ModuleContext) - .FirstOrDefault(); - - return item; - } - - /// - /// Determines the module for a given application context and module type. - /// - /// The context of the application. - /// The module class. - /// The context of the module or null. - public IModuleContext GetModule(IApplicationContext applicationContext, Type moduleClass) - { - return GetModule(applicationContext, moduleClass.FullName.ToLower()); - } - - /// - /// Determines the module for a given plugin context and module id. - /// - /// The context of the plugin. - /// The modul id. - /// An enumeration of the module contexts for the given plugin and module id. - public IEnumerable GetModules(IPluginContext pluginContext, string moduleId) - { - return GetModuleItems(pluginContext) - .Where(x => x.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .SelectMany(x => x.Dictionary.Values) - .Select(x => x.ModuleContext); - } - - /// - /// Determines the module for a given plugin context and module id. - /// - /// The context of the plugin. - /// The modul id. - /// An enumeration of the module contexts for the given plugin and module id. - public IEnumerable GetModules(IApplicationContext applicationContext) - { - return _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.Dictionary.ContainsKey(applicationContext)) - .Select(x => x.Dictionary[applicationContext]) - .Select(x => x.ModuleContext); - } - - /// - /// Returns the modules for a given plugin. - /// - /// The context of the plugin. - /// An enumeration of the module contexts for the given plugin. - internal IEnumerable GetModuleItems(IPluginContext pluginContext) - { - if (pluginContext == null || !_dictionary.ContainsKey(pluginContext)) - { - return []; - } - - return _dictionary[pluginContext].Values; - } - - /// - /// Boots the modules of a plugin. - /// - /// The context of the plugin containing the modules. - public void Boot(IPluginContext pluginContext) - { - foreach (var moduleItem in GetModuleItems(pluginContext)) - { - // initialize module - moduleItem.Boot(); - } - } - - /// - /// Terminate modules of a plugin. - /// - /// The context of the plugin containing the modules. - public void ShutDown(IPluginContext pluginContext) - { - foreach (var moduleItem in GetModuleItems(pluginContext)) - { - // terminate module - moduleItem.ShutDown(); - } - } - - /// - /// Terminate modules of a application. - /// - /// The context of the application containing the modules. - public void ShutDown(IApplicationContext applicationContext) - { - foreach (var moduleItem in _dictionary.Values.SelectMany(x => x.Values)) - { - // terminate module - moduleItem.ShutDown(applicationContext); - } - } - - /// - /// Removes all modules associated with the specified plugin context. - /// - /// The context of the plugin that contains the modules to remove. - public void Remove(IPluginContext pluginContext) - { - if (pluginContext == null) - { - return; - } - - if (!_dictionary.ContainsKey(pluginContext)) - { - return; - } - - ShutDown(pluginContext); - - foreach (var moduleItem in _dictionary[pluginContext].Values) - { - moduleItem.Dispose(); - } - - _dictionary.Remove(pluginContext); - } - - /// - /// Raises the AddModule event. - /// - /// The module context. - private void OnAddModule(IModuleContext moduleContext) - { - AddModule?.Invoke(this, moduleContext); - } - - /// - /// Raises the RemoveModule event. - /// - /// The module context. - private void OnRemoveModule(IModuleContext moduleContext) - { - RemoveModule?.Invoke(this, moduleContext); - } - - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - foreach (var moduleContext in GetModuleItems(pluginContext)) - { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate - ( - "webexpress:modulemanager.module", - moduleContext.ModuleId, - string.Join(",", moduleContext.Applications) - ) - ); - } - } - } -} diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index 6570d0d..a034131 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -477,5 +477,12 @@ private void OnRemovePackage(PackageCatalogItem item) public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index 7415ea3..d963c30 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -1,4 +1,4 @@ -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebPage { diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index c9df8bc..b13a867 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebPage { diff --git a/src/WebExpress.WebCore/WebPage/IPageManager.cs b/src/WebExpress.WebCore/WebPage/IPageManager.cs index a573e67..5ab7e4f 100644 --- a/src/WebExpress.WebCore/WebPage/IPageManager.cs +++ b/src/WebExpress.WebCore/WebPage/IPageManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebPage @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebPage /// /// The page manager manages page elements, which can be called with a URI (Uniform Resource Identifier). /// - public interface IPageManager : IEndpointManager + public interface IPageManager : IComponentManager { /// /// An event that fires when an page is added. @@ -51,41 +51,32 @@ public interface IPageManager : IEndpointManager /// Returns an enumeration of page contextes. /// /// The page type. - /// The context of the module. + /// The context of the application. /// An enumeration of page contextes. - IEnumerable GetPages(Type pageType, IModuleContext moduleContext); + IEnumerable GetPages(Type pageType, IApplicationContext applicationContext); /// /// Returns an enumeration of page contextes. /// /// The page type. - /// The context of the module. + /// The context of the application. /// An enumeration of page contextes. - IEnumerable GetPages(IModuleContext moduleContext) where T : IPage; + IEnumerable GetPages(IApplicationContext applicationContext) where T : IPage; /// /// Returns the page context. /// - /// The context of the module. + /// The context of the application. /// The page id. /// An page context or null. - IPageContext GetPage(IModuleContext moduleContext, string pageId); - - /// - /// Returns the page context. - /// - /// The context of the module. - /// The page type. - /// An page context or null. - IPageContext GetPage(IModuleContext moduleContext, Type pageType); + IPageContext GetPage(IApplicationContext applicationContext, string pageId); /// /// Returns the page context. /// /// The application id. - /// The module id. /// The page id. /// An page context or null. - IPageContext GetPage(string applicationId, string moduleId, string pageId); + IPageContext GetPage(string applicationId, string pageId); } } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs index e0bd823..e6e9268 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -1,13 +1,145 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebPage.Model { /// + /// Represents a dictionary that maps plugin contexts to application contexts, page types, and page items. /// key = plugin context - /// value = { key = resource id, value = ressource item } + /// value = { key = page type, value = page item } /// - internal class PageDictionary : Dictionary> + internal class PageDictionary : Dictionary>> { + /// + /// Adds a page item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The page item. + /// True if the page item was added successfully, false if an element with the same status code already exists. + public bool AddPageItem(IPluginContext pluginContext, IApplicationContext applicationContext, PageItem pageItem) + { + var type = pageItem.PageClass; + + if (!typeof(IPage).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = []; + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = new Dictionary(); + } + + var pageDict = appContextDict[applicationContext]; + + if (!pageDict.ContainsKey(type)) + { + pageDict[type] = pageItem; + return true; + } + + return false; // item with the same page class already exists + } + + /// + /// Removes a page from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemovePage(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IPage + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var pageDict = appContextDict[applicationContext]; + + if (pageDict.ContainsKey(type)) + { + pageDict.Remove(type); + + if (pageDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the page items from the dictionary. + /// + /// The type of page. + /// The application context. + /// An IEnumerable of page items + public IEnumerable GetPageItems(IApplicationContext applicationContext) where T : IPage + { + return GetPageItems(applicationContext, typeof(T)); + } + + /// + /// Returns the page items from the dictionary. + /// + /// The application context. + /// The type of page. + /// An IEnumerable of page items + public IEnumerable GetPageItems(IApplicationContext applicationContext, Type pageType) + { + if (!typeof(IPage).IsAssignableFrom(pageType)) + { + return Enumerable.Empty(); + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var pageDict = appContextDict[applicationContext]; + + if (pageDict.ContainsKey(pageType)) + { + return [pageDict[pageType]]; + } + } + } + + return []; + } + + /// + /// Returns all page contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of page contexts. + public IEnumerable GetPages(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .Select(x => x.ContextPath as IPageContext); + } } } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 74341e4..038a9ce 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage.Model @@ -17,30 +13,15 @@ internal class PageItem : IDisposable { private readonly IPageManager _pageManager; - /// - /// An event that fires when an page is added. - /// - public event EventHandler AddPage; - - /// - /// An event that fires when an page is removed. - /// - public event EventHandler RemovePage; - - /// - /// Returns or sets the page id. - /// - public string PageId { get; set; } - /// /// Returns or sets the resource title. /// public string Title { get; set; } /// - /// Returns or sets the parent id. + /// Returns or sets the parent type. /// - public string ParentId { get; set; } + public Type ParentType { get; set; } /// /// Returns or sets the type of page. @@ -52,11 +33,6 @@ internal class PageItem : IDisposable /// public IPage Instance { get; set; } - /// - /// Returns or sets the module id. - /// - public string ModuleId { get; set; } - /// /// Returns the scope names that provides the resource. The scope name /// is a string with a name (e.g. global, admin), which can be used by elements to @@ -100,20 +76,9 @@ internal class PageItem : IDisposable public ILog Log { get; internal set; } /// - /// Returns the directory where the module instances are listed. - /// - private IDictionary Dictionary { get; } - = new Dictionary(); - - /// - /// Returns the associated module contexts. + /// Returns the page context. /// - public IEnumerable ModuleContexts => Dictionary.Keys; - - /// - /// Returns the page contexts. - /// - public IEnumerable PageContexts => Dictionary.Values; + public IPageContext PageContext { get; internal set; } /// /// Initializes a new instance of the class. @@ -124,106 +89,12 @@ internal PageItem(IPageManager pageManager) _pageManager = pageManager; } - /// - /// Adds an module assignment - /// - /// The context of the module. - public void AddModule(IModuleContext moduleContext) - { - // only if no instance has been created yet - if (Dictionary.ContainsKey(moduleContext)) - { - Log.Warning(message: I18N.Translate("webexpress:pagemanager.addresource.duplicate", PageId, moduleContext.ModuleId)); - - return; - } - - // create context - var pageContext = new PageContext(moduleContext, _pageManager, this) - { - Scopes = Scopes, - Conditions = Conditions, - EndpointId = PageId, - PageTitle = Title, - Cache = Cache, - IncludeSubPaths = IncludeSubPaths - }; - - if - ( - !Optional || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.{PageId.ToLower()}") || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.*") || - moduleContext.ApplicationContext.Options.Where(x => Regex.Match($"{ModuleId.ToLower()}.{PageId.ToLower()}", x).Success).Any() - ) - { - Dictionary.Add(moduleContext, pageContext); - OnAddPage(pageContext); - } - } - - /// - /// Remove an module assignment. - /// - /// The context of the module. - public void DetachModule(IModuleContext moduleContext) - { - // not an assignment has been created yet - if (!Dictionary.ContainsKey(moduleContext)) - { - return; - } - - foreach (var resourceContext in Dictionary.Values) - { - OnRemovePage(resourceContext); - } - - Dictionary.Remove(moduleContext); - } - - /// - /// Checks whether a module context is already assigned to the item. - /// - /// The module context. - /// True a mapping exists, false otherwise. - public bool IsAssociatedWithModule(IModuleContext moduleContext) - { - return Dictionary.ContainsKey(moduleContext); - } - - /// - /// Raises the AddPage event. - /// - /// The page context. - private void OnAddPage(IPageContext pageContext) - { - AddPage?.Invoke(this, pageContext); - } - - /// - /// Raises the RemovePage event. - /// - /// The page context. - private void OnRemovePage(IPageContext pageContext) - { - RemovePage?.Invoke(this, pageContext); - } - /// /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. /// public void Dispose() { - foreach (Delegate d in AddPage.GetInvocationList()) - { - AddPage -= (EventHandler)d; - } - - foreach (Delegate d in RemovePage.GetInvocationList()) - { - RemovePage -= (EventHandler)d; - } + } /// @@ -232,7 +103,7 @@ public void Dispose() /// The resource element in its string representation. public override string ToString() { - return $"Page '{PageId}'"; + return $"Page: '{PageContext?.EndpointId}'"; } } } diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index af2c526..c53e478 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; -using WebExpress.WebCore.WebPage.Model; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -15,18 +14,20 @@ namespace WebExpress.WebCore.WebPage /// public class PageContext : IPageContext { - private readonly IPageManager _pageManager; - private readonly PageItem _pageItem; + private readonly IEndpointManager _endpointManager; + private readonly Type _parentType; + private readonly UriResource _contextPath; + private readonly IUriPathSegment _pathSegment; /// /// Returns the associated plugin context. /// - public IPluginContext PluginContext { get; private set; } + public IPluginContext PluginContext { get; internal set; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - public IModuleContext ModuleContext { get; private set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns the scope names that provides the resource. The scope name @@ -53,10 +54,7 @@ public class PageContext : IPageContext /// /// Returns the parent or null if not used. /// - public IEndpointContext ParentContext => _pageManager.Pages - .Where(x => !string.IsNullOrWhiteSpace(_pageItem.ParentId)) - .Where(x => x.EndpointId.Equals(_pageItem.ParentId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) + public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) .FirstOrDefault(); /// @@ -79,30 +77,31 @@ public UriResource ContextPath var parentContext = ParentContext; if (parentContext != null) { - return UriResource.Combine(ParentContext?.Uri, _pageItem.ContextPath); + return UriResource.Combine(ParentContext?.Uri, _contextPath); } - return UriResource.Combine(ModuleContext.ContextPath, _pageItem.ContextPath); + return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); } } /// /// Returns the uri. /// - public UriResource Uri => ContextPath.Append(_pageItem.PathSegment); + public UriResource Uri => ContextPath.Append(_pathSegment); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified parent type and context path. /// - /// The module context. - /// The resource manager. - /// The page item or null. - internal PageContext(IModuleContext moduleContext, IPageManager pageManager, PageItem pageItem = null) + /// The endpoint manager responsible for managing endpoints. + /// The type of the parent resource. + /// The context path of the resource. + /// The path segment of the resource. + public PageContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) { - PluginContext = moduleContext?.PluginContext; - ModuleContext = moduleContext; - _pageManager = pageManager; - _pageItem = pageItem; + _endpointManager = endpointManager; + _parentType = parentType; + _contextPath = contextPath; + _pathSegment = pathSegment; } /// @@ -111,7 +110,7 @@ internal PageContext(IModuleContext moduleContext, IPageManager pageManager, Pag /// A string that represents the current object. public override string ToString() { - return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; + return $"Page: {EndpointId}"; } } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index bce8a39..9326de7 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -3,15 +3,15 @@ using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPage.Model; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; -using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage @@ -21,7 +21,7 @@ namespace WebExpress.WebCore.WebPage /// public class PageManager : IPageManager { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly PageDictionary _dictionary = []; @@ -40,7 +40,8 @@ public class PageManager : IPageManager /// public IEnumerable Pages => _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts); + .SelectMany(x => x.Values) + .Select(x => x.PageContext); /// /// Initializes a new instance of the class. @@ -49,54 +50,39 @@ public class PageManager : IPageManager /// The reference to the context of the host. private PageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { - _componentManager = componentManager; - - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; - - _componentManager.ModuleManager.AddModule += (sender, moduleContext) => - { - AssignToModule(moduleContext); - }; + _componentHub = componentManager; - _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => - { - DetachFromModule(moduleContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentManager.SitemapManager.Register + _componentHub.EndpointManager.Register ( new EndpointRegistration() { - Factory = (resourceContext, uri, culture) => CreatePageInstance(resourceContext as IPageContext, culture), - ContextResolver = (type, moduleContext) => moduleContext != null ? GetPages(type, moduleContext) : GetPages(type), - EndpointResolver = () => Pages, - HandleRequest = (endpoint, endpontContext, request) => + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetPages(type, applicationContext) : GetPages(type), + EndpointsResolver = () => Pages, + HandleRequest = (request, endpontContext) => { - var type = endpoint.GetType(); + var page = CreatePageInstance(endpontContext as IPageContext, request.Culture); + var pageType = page.GetType(); var context = default(IRenderContext); var pageContetx = endpontContext as IPageContext; - if (type.IsGenericType) + if (pageType.IsGenericType) { - var typeOfT = type.GetGenericArguments()[0]; - var parameters = new object[] { endpoint as IPage, endpontContext as IPageContext, request }; + var typeOfT = pageType.GetGenericArguments()[0]; + var parameters = new object[] { page, endpontContext as IPageContext, request }; context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; } else { - context = new RenderContext(endpontContext.ModuleContext?.ApplicationContext, request, pageContetx.Scopes); + context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); } - (endpoint as IPage).Process(context); + page.Process(context); return new ResponseOK() { @@ -121,13 +107,14 @@ private PageManager(IComponentHub componentManager, IHttpServerContext httpServe /// An enumeration of page contexts. public IEnumerable GetPages(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value.PageContext); } - return _dictionary[pluginContext].Values - .SelectMany(x => x.PageContexts); + return []; } /// @@ -149,7 +136,7 @@ public IEnumerable GetPages(Type pageType) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) + .SelectMany(x => x.Values) .Where(x => x.PageClass.Equals(pageType)) .Select(x => x.PageContext); } @@ -158,15 +145,15 @@ public IEnumerable GetPages(Type pageType) /// Returns an enumeration of page contextes. /// /// The page type. - /// The context of the module. + /// The context of the application. /// An enumeration of page contextes. - public IEnumerable GetPages(Type pageType, IModuleContext moduleContext) + public IEnumerable GetPages(Type pageType, IApplicationContext applicationContext) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) - .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) .Where(x => x.PageClass.Equals(pageType)) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) .Select(x => x.PageContext); } @@ -174,52 +161,31 @@ public IEnumerable GetPages(Type pageType, IModuleContext moduleCo /// Returns an enumeration of page contextes. /// /// The page type. - /// The context of the module. + /// The context of the application. /// An enumeration of page contextes. - public IEnumerable GetPages(IModuleContext moduleContext) where T : IPage + public IEnumerable GetPages(IApplicationContext applicationContext) where T : IPage { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) - .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) .Where(x => x.PageClass.Equals(typeof(T))) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) .Select(x => x.PageContext); } /// /// Returns the page context. /// - /// The context of the module. + /// The context of the application. /// The page id. /// An page context or null. - public IPageContext GetPage(IModuleContext moduleContext, string pageId) + public IPageContext GetPage(IApplicationContext applicationContext, string pageId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) - .Where(x => x.PageContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.PageContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.PageContext.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.PageContext) - .FirstOrDefault(); - } - - /// - /// Returns the page context. - /// - /// The context of the module. - /// The page type. - /// An page context or null. - public IPageContext GetPage(IModuleContext moduleContext, Type pageType) - { - return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts, (x, y) => new { x.PageClass, PageContext = y }) - .Where(x => x.PageContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.PageContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.PageContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.PageClass.Equals(pageType)) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) + .Where(x => x.PageContext.EndpointId.Equals(pageId)) .Select(x => x.PageContext) .FirstOrDefault(); } @@ -228,18 +194,16 @@ public IPageContext GetPage(IModuleContext moduleContext, Type pageType) /// Returns the page context. /// /// The application id. - /// The module id. /// The page id. /// An page context or null. - public IPageContext GetPage(string applicationId, string moduleId, string pageId) + public IPageContext GetPage(string applicationId, string pageId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.PageContexts) - .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) - .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) + .Where(x => x.PageContext.ApplicationContext.ApplicationId.Equals(applicationId)) + .Where(x => x.PageContext.EndpointId.Equals(pageId)) + .Select(x => x.PageContext) .FirstOrDefault(); } @@ -253,11 +217,12 @@ private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) - .FirstOrDefault(x => x.PageContexts.Contains(pageContext)); + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.PageContext.Equals(pageContext)); if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _componentManager); + var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _componentHub); if (instance is II18N i18n) { @@ -276,20 +241,44 @@ private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) } /// - /// Discovers and registers pages from the specified plugin. + /// Discovers and binds pages to an application. /// - /// A context of a plugin whose pages are to be registered. - public void Register(IPluginContext pluginContext) + /// The context of the plugin whose pages are to be associated. + private void Register(IPluginContext pluginContext) { if (_dictionary.ContainsKey(pluginContext)) { return; } - var assembly = pluginContext?.Assembly; + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } - _dictionary.Add(pluginContext, []); - var dict = _dictionary[pluginContext]; + /// + /// Discovers and binds pages to an application. + /// + /// The context of the application whose pages are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers pages for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; foreach (var resourceType in assembly.GetTypes() .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) @@ -298,27 +287,23 @@ public void Register(IPluginContext pluginContext) var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); var title = resourceType.Name; - var parent = default(string); + var parent = default(Type); var contextPath = string.Empty; var includeSubPaths = false; - var moduleId = string.Empty; var scopes = new List(); var conditions = new List(); - var optional = false; var cache = false; foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { - var buf = typeof(ModuleAttribute<>); - if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) { @@ -328,10 +313,6 @@ public void Register(IPluginContext pluginContext) { includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); } - else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) - { - moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -341,17 +322,11 @@ public void Register(IPluginContext pluginContext) { cache = true; } - else if (customAttribute.AttributeType == typeof(OptionalAttribute)) - { - optional = true; - } } foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute)))) { - var buf = typeof(ModuleAttribute<>); - if (customAttribute.AttributeType == typeof(TitleAttribute)) { title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); @@ -367,83 +342,54 @@ public void Register(IPluginContext pluginContext) scopes.Add(resourceType.FullName?.ToLower()); } - if (string.IsNullOrEmpty(moduleId)) + // assign the job to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { - // no module specified - _httpServerContext.Log.Warning - ( - I18N.Translate - ( - "webexpress:pagemanager.moduleless", - id - ) - ); - - continue; - } + var pageContext = new PageContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) + { + PageTitle = title, + EndpointId = resourceType.FullName.ToLower(), + PluginContext = pluginContext, + ApplicationContext = applicationContext, + Cache = cache, + Scopes = scopes, + Conditions = conditions, + IncludeSubPaths = includeSubPaths + }; - if (!dict.ContainsKey(id)) - { - var pageItem = new PageItem(_componentManager.PageManager) + var pageItem = new PageItem(_componentHub.PageManager) { - PageId = id, Title = title, - ParentId = parent, + PageContext = pageContext, + ParentType = parent, PageClass = resourceType, - ModuleId = moduleId, Scopes = scopes, Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, PathSegment = segment.ToPathSegment(), - Optional = optional, Log = _httpServerContext?.Log }; - pageItem.AddPage += (s, e) => + if (_dictionary.AddPageItem(pluginContext, applicationContext, pageItem)) { - OnAddPage(e); - }; - - pageItem.RemovePage += (s, e) => - { - OnRemovePage(e); - }; + OnAddPage(pageContext); - dict.Add(id, pageItem); - - _httpServerContext?.Log.Debug - ( - I18N.Translate + _httpServerContext?.Log.Debug ( - "webexpress:pagemanager.addresource", - id, - moduleId - ) - ); - } - - // assign the resource to existing modules. - foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) - { - AssignToModule(moduleContext); + I18N.Translate + ( + "webexpress:pagemanager.addresource", + id, + applicationContext.ApplicationId + ) + ); + } } } } - /// - /// Discovers and registers resources from the specified plugin. - /// - /// A list with plugin contexts that contain the resources. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); - } - } - /// /// Removes all pages associated with the specified plugin context. /// @@ -461,8 +407,10 @@ public void Remove(IPluginContext pluginContext) return; } - foreach (var resourceItem in _dictionary[pluginContext].Values) + foreach (var resourceItem in _dictionary[pluginContext].Values + .SelectMany(x => x.Values)) { + OnRemovePage(resourceItem.PageContext); resourceItem.Dispose(); } @@ -470,31 +418,28 @@ public void Remove(IPluginContext pluginContext) } /// - /// Assign existing resources to the module. + /// Removes all jobs associated with the specified application context. /// - /// The context of the module. - private void AssignToModule(IModuleContext moduleContext) + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + if (applicationContext == null) { - resourceItem.AddModule(moduleContext); + return; } - } - /// - /// Remove an existing modules to the application. - /// - /// The context of the module. - private void DetachFromModule(IModuleContext moduleContext) - { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + foreach (var pluginDict in _dictionary.Values) { - resourceItem.DetachModule(moduleContext); + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var resourceItem in appDict.Values) + { + OnRemovePage(resourceItem.PageContext); + resourceItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); } } @@ -505,12 +450,14 @@ private void DetachFromModule(IModuleContext moduleContext) /// An enumeration of pages items. private IEnumerable GetPageItems(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value); } - return _dictionary[pluginContext].Values; + return []; } /// @@ -531,6 +478,46 @@ private void OnRemovePage(IPageContext pageContext) RemovePage?.Invoke(this, pageContext); } + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Information about the component is collected and prepared for output in the log. /// @@ -547,11 +534,22 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in I18N.Translate ( "webexpress:pagemanager.resource", - resourcenItem.PageId, - string.Join(",", resourcenItem.ModuleId) + resourcenItem?.PageContext?.EndpointId, + string.Join(",", resourcenItem?.PageContext?.ApplicationContext?.ApplicationId) ) ); } } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + } } } diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs index bf8ac07..5dcd747 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPlugin @@ -37,5 +38,19 @@ public interface IPluginManager : IComponentManager /// The type of the plugin. /// The plugin context. IPluginContext GetPlugin(Type plugin); + + /// + /// Returns all plugins that have associated applications. + /// + /// The application context to filter plugins. + /// An enumerable collection of plugin contexts with applications. + IEnumerable GetPlugins(IApplicationContext applicationContext); + + /// + /// Returns all ApplicationContext instances associated with a plugin. + /// + /// The context of the plugin. + /// A collection of ApplicationContext instances. + IEnumerable GetAssociatedApplications(IPluginContext pluginContext); } } diff --git a/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs b/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs index f9f9a01..47cd206 100644 --- a/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs +++ b/src/WebExpress.WebCore/WebPlugin/Model/PluginItem.cs @@ -34,6 +34,11 @@ internal class PluginItem /// public IEnumerable Dependencies { get; internal set; } = []; + /// + /// The types of applications that the plugin supports. + /// + public IEnumerable ApplicationTypes { get; internal set; } = []; + /// /// Thread termination token. /// diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 2835299..d348972 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Threading.Tasks; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebLog; @@ -185,6 +186,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex var description = type.Assembly.GetCustomAttribute()?.Description; var dependencies = new List(); var hasUnfulfilledDependencies = false; + var applicationTypes = new List(); foreach (var customAttribute in type.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPluginAttribute)))) @@ -205,6 +207,21 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex { dependencies.Add(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); } + else if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) + { + applicationTypes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()); + } + } + + if (!applicationTypes.Any()) + { + // no application specified + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:pluginmanager.applicationless", id) + ); + + break; } var pluginContext = new PluginContext() @@ -241,7 +258,8 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginClass = type, PluginContext = pluginContext, Plugin = ComponentActivator.CreateInstance(type, pluginContext, _componentManager), - Dependencies = dependencies + Dependencies = dependencies, + ApplicationTypes = applicationTypes }); _httpServerContext.Log.Debug @@ -405,6 +423,43 @@ public IPluginContext GetPlugin(Type plugin) .FirstOrDefault(); } + /// + /// Returns all plugins that have associated applications. + /// + /// The application context to filter plugins. + /// An enumerable collection of plugin contexts with applications. + public IEnumerable GetPlugins(IApplicationContext applicationContext) + { + return _dictionary.Values + .Where(x => x.ApplicationTypes != null) + .Where(x => x.ApplicationTypes.Select(x => _componentManager.ApplicationManager.GetApplications(x)) + .SelectMany(x => x) + .Where(x => x.ApplicationId == applicationContext.ApplicationId) + .Any()) + .Select(x => x.PluginContext); + } + + /// + /// Returns all ApplicationContext instances associated with a plugin. + /// + /// The context of the plugin. + /// A collection of ApplicationContext instances. + public IEnumerable GetAssociatedApplications(IPluginContext pluginContext) + { + var pluginItem = GetPluginItem(pluginContext); + + if (pluginItem == null) + { + return []; + } + + return pluginItem.ApplicationTypes? + .Select(x => _componentManager.ApplicationManager.GetApplications(x)) + .SelectMany(x => x) + .Where(x => x != null) ?? []; + } + + /// /// Returns a plugin item based on the context. /// @@ -601,5 +656,12 @@ private void Logging() public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebResource/IResource.cs b/src/WebExpress.WebCore/WebResource/IResource.cs index 32e9a1d..583e12d 100644 --- a/src/WebExpress.WebCore/WebResource/IResource.cs +++ b/src/WebExpress.WebCore/WebResource/IResource.cs @@ -1,4 +1,4 @@ -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebResource diff --git a/src/WebExpress.WebCore/WebResource/IResourceContext.cs b/src/WebExpress.WebCore/WebResource/IResourceContext.cs index dde1df0..c8bed1c 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceContext.cs @@ -1,4 +1,4 @@ -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebResource { diff --git a/src/WebExpress.WebCore/WebResource/IResourceManager.cs b/src/WebExpress.WebCore/WebResource/IResourceManager.cs index 5a021e3..8e19486 100644 --- a/src/WebExpress.WebCore/WebResource/IResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/IResourceManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebResource @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebResource /// /// The resource manager manages resources elements, which can be called with a URI (Uniform Resource Identifier). /// - public interface IResourceManager : IEndpointManager + public interface IResourceManager : IComponentManager { /// /// An event that fires when an resource is added. @@ -51,41 +51,32 @@ public interface IResourceManager : IEndpointManager /// Returns an enumeration of resource contextes. /// /// The resource type. - /// The context of the module. + /// The context of the application. /// An enumeration of resource contextes. - IEnumerable GetResorces(Type resourceType, IModuleContext moduleContext); + IEnumerable GetResorces(Type resourceType, IApplicationContext applicationContext); /// /// Returns an enumeration of resource contextes. /// /// The resource type. - /// The context of the module. + /// The context of the application. /// An enumeration of resource contextes. - IEnumerable GetResorces(IModuleContext moduleContext) where T : IResource; + IEnumerable GetResorces(IApplicationContext applicationContext) where T : IResource; /// /// Returns the resource context. /// - /// The context of the module. + /// The context of the application. /// The resource id. /// An resource context or null. - IResourceContext GetResorce(IModuleContext moduleContext, string resourceId); - - /// - /// Returns the resource context. - /// - /// The context of the module. - /// The resource type. - /// An resource context or null. - IResourceContext GetResorce(IModuleContext moduleContext, Type resourceType); + IResourceContext GetResorce(IApplicationContext applicationContext, string resourceId); /// /// Returns the resource context. /// /// The application id. - /// The module id. /// The resource id. /// An resource context or null. - IResourceContext GetResorce(string applicationId, string moduleId, string resourceId); + IResourceContext GetResorce(string applicationId, string resourceId); } } diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs index 7e5ef5f..6c1dbd4 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs @@ -1,13 +1,145 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebResource.Model { /// + /// Represents a dictionary that maps plugin contexts to application contexts, resource types, and resource items. /// key = plugin context - /// value = { key = resource id, value = ressource item } + /// value = { key = resource type, value = ressource item } /// - internal class ResourceDictionary : Dictionary> + internal class ResourceDictionary : Dictionary>> { + /// + /// Adds a resource item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The resource item. + /// True if the resource item was added successfully, false if an element with the same status code already exists. + public bool AddResourceItem(IPluginContext pluginContext, IApplicationContext applicationContext, ResourceItem resourceItem) + { + var type = resourceItem.ResourceClass; + + if (!typeof(IResource).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = new Dictionary>(); + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = new Dictionary(); + } + + var resourceDict = appContextDict[applicationContext]; + + if (!resourceDict.ContainsKey(type)) + { + resourceDict[type] = resourceItem; + return true; + } + + return false; // item with the same resource class already exists + } + + /// + /// Removes a resource from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemoveResource(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IResource + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var resourceDict = appContextDict[applicationContext]; + + if (resourceDict.ContainsKey(type)) + { + resourceDict.Remove(type); + + if (resourceDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the resource items from the dictionary. + /// + /// The type of resource. + /// The application context. + /// An IEnumerable of resource items + public IEnumerable GetResourceItems(IApplicationContext applicationContext) where T : IResource + { + return GetResourceItems(applicationContext, typeof(T)); + } + + /// + /// Returns the resource items from the dictionary. + /// + /// The application context. + /// The type of resource. + /// An IEnumerable of resource items + public IEnumerable GetResourceItems(IApplicationContext applicationContext, Type resourceType) + { + if (!typeof(IResource).IsAssignableFrom(resourceType)) + { + return Enumerable.Empty(); + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var resourceDict = appContextDict[applicationContext]; + + if (resourceDict.ContainsKey(resourceType)) + { + return new List { resourceDict[resourceType] }; + } + } + } + + return []; + } + + /// + /// Returns all resource contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of resource contexts. + public IEnumerable GetResources(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .Select(x => x.ContextPath as IResourceContext); + } } } diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index d44a5e7..7b43008 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -1,12 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource.Model @@ -19,29 +15,9 @@ internal class ResourceItem : IDisposable private readonly IResourceManager _resourceManager; /// - /// An event that fires when an ressource is added. + /// Returns or sets the parent type. /// - public event EventHandler AddResource; - - /// - /// An event that fires when an resource is removed. - /// - public event EventHandler RemoveResource; - - /// - /// Returns or sets the resource id. - /// - public string ResourceId { get; set; } - - /// - /// Returns or sets the resource title. - /// - //public string Title { get; set; } - - /// - /// Returns or sets the parent id. - /// - public string ParentId { get; set; } + public Type ParentType { get; set; } /// /// Returns or sets the type of resource. @@ -53,11 +29,6 @@ internal class ResourceItem : IDisposable /// public IEndpoint Instance { get; set; } - /// - /// Returns or sets the module id. - /// - public string ModuleId { get; set; } - /// /// Returns the scope names that provides the resource. The scope name /// is a string with a name (e.g. global, admin), which can be used by elements to @@ -90,31 +61,15 @@ internal class ResourceItem : IDisposable /// public bool Cache { get; set; } - /// - /// Returns whether it is a optional resource. - /// - public bool Optional { get; set; } - /// /// Returns the log to write status messages to the console and to a log file. /// public ILog Log { get; internal set; } /// - /// Returns the directory where the module instances are listed. - /// - private IDictionary Dictionary { get; } - = new Dictionary(); - - /// - /// Returns the associated module contexts. - /// - public IEnumerable ModuleContexts => Dictionary.Keys; - - /// - /// Returns the resource contexts. + /// Returns the resource context. /// - public IEnumerable ResourceContexts => Dictionary.Values; + public IResourceContext ResourceContext { get; internal set; } /// /// Initializes a new instance of the class. @@ -125,105 +80,12 @@ internal ResourceItem(IResourceManager resourceManager) _resourceManager = resourceManager; } - /// - /// Adds an module assignment - /// - /// The context of the module. - public void AddModule(IModuleContext moduleContext) - { - // only if no instance has been created yet - if (Dictionary.ContainsKey(moduleContext)) - { - Log.Warning(message: I18N.Translate("webexpress:resourcemanager.addresource.duplicate", ResourceId, moduleContext.ModuleId)); - - return; - } - - // create context - var resourceContext = new ResourceContext(moduleContext, _resourceManager, this) - { - //Scopes = Scopes, - Conditions = Conditions, - EndpointId = ResourceId, - Cache = Cache, - IncludeSubPaths = IncludeSubPaths - }; - - if - ( - !Optional || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.{ResourceId.ToLower()}") || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.*") || - moduleContext.ApplicationContext.Options.Where(x => Regex.Match($"{ModuleId.ToLower()}.{ResourceId.ToLower()}", x).Success).Any() - ) - { - Dictionary.Add(moduleContext, resourceContext); - OnAddResource(resourceContext); - } - } - - /// - /// Remove an module assignment. - /// - /// The context of the module. - public void DetachModule(IModuleContext moduleContext) - { - // not an assignment has been created yet - if (!Dictionary.ContainsKey(moduleContext)) - { - return; - } - - foreach (var resourceContext in Dictionary.Values) - { - OnRemoveResource(resourceContext); - } - - Dictionary.Remove(moduleContext); - } - - /// - /// Checks whether a module context is already assigned to the item. - /// - /// The module context. - /// True a mapping exists, false otherwise. - public bool IsAssociatedWithModule(IModuleContext moduleContext) - { - return Dictionary.ContainsKey(moduleContext); - } - - /// - /// Raises the AddResource event. - /// - /// The resource context. - private void OnAddResource(IResourceContext resourceContext) - { - AddResource?.Invoke(this, resourceContext); - } - - /// - /// Raises the RemoveResource event. - /// - /// The resource context. - private void OnRemoveResource(IResourceContext resourceContext) - { - RemoveResource?.Invoke(this, resourceContext); - } - /// /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. /// public void Dispose() { - foreach (Delegate d in AddResource.GetInvocationList()) - { - AddResource -= (EventHandler)d; - } - - foreach (Delegate d in RemoveResource.GetInvocationList()) - { - RemoveResource -= (EventHandler)d; - } + } /// @@ -232,7 +94,7 @@ public void Dispose() /// The resource element in its string representation. public override string ToString() { - return $"Resource '{ResourceId}'"; + return $"Resource '{ResourceContext?.EndpointId}'"; } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index e16b54b..c7f44df 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource @@ -15,23 +14,25 @@ namespace WebExpress.WebCore.WebResource /// public class ResourceContext : IResourceContext { - private readonly IResourceManager _resourceManager; - private readonly ResourceItem _resourceItem; + private readonly IEndpointManager _endpointManager; + private readonly Type _parentType; + private readonly UriResource _contextPath; + private readonly IUriPathSegment _pathSegment; /// /// Returns the associated plugin context. /// - public IPluginContext PluginContext { get; private set; } + public IPluginContext PluginContext { get; internal set; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - public IModuleContext ModuleContext { get; private set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns the conditions that must be met for the resource to be active. /// - public IEnumerable Conditions { get; internal set; } = new List(); + public IEnumerable Conditions { get; internal set; } = []; /// /// Returns the resource id. @@ -41,10 +42,7 @@ public class ResourceContext : IResourceContext /// /// Returns the parent or null if not used. /// - public IEndpointContext ParentContext => _resourceManager.Resources - .Where(x => !string.IsNullOrWhiteSpace(_resourceItem.ParentId)) - .Where(x => x.EndpointId.Equals(_resourceItem.ParentId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) + public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) .FirstOrDefault(); /// @@ -67,30 +65,31 @@ public UriResource ContextPath var parentContext = ParentContext; if (parentContext != null) { - return UriResource.Combine(ParentContext?.Uri, _resourceItem.ContextPath); + return UriResource.Combine(ParentContext?.Uri, _contextPath); } - return UriResource.Combine(ModuleContext.ContextPath, _resourceItem.ContextPath); + return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); } } /// /// Returns the uri. /// - public UriResource Uri => ContextPath.Append(_resourceItem.PathSegment); + public UriResource Uri => ContextPath.Append(_pathSegment); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. /// - /// The module context. - /// The resource manager. - /// The resource item or null. - internal ResourceContext(IModuleContext moduleContext, IResourceManager resourceManager, ResourceItem resourceItem = null) + /// The endpoint manager responsible for managing endpoints. + /// The type of the parent resource. + /// The context path of the resource. + /// The path segment of the resource. + public ResourceContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) { - PluginContext = moduleContext?.PluginContext; - ModuleContext = moduleContext; - _resourceManager = resourceManager; - _resourceItem = resourceItem; + _endpointManager = endpointManager; + _parentType = parentType; + _contextPath = contextPath; + _pathSegment = pathSegment; } /// @@ -99,7 +98,7 @@ internal ResourceContext(IModuleContext moduleContext, IResourceManager resource /// A string that represents the current object. public override string ToString() { - return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; + return $"Resource: {EndpointId}"; } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index d160f26..2e154e3 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -3,13 +3,13 @@ using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource.Model; -using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebUri; @@ -18,9 +18,9 @@ namespace WebExpress.WebCore.WebResource /// /// The resource manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). /// - public sealed class ResourceManager : IResourceManager, IComponentManagerPlugin, ISystemComponent + public sealed class ResourceManager : IResourceManager, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly ResourceDictionary _dictionary = []; @@ -39,45 +39,36 @@ public sealed class ResourceManager : IResourceManager, IComponentManagerPlugin, /// public IEnumerable Resources => _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts); + .SelectMany(x => x.Values) + .Select(x => x.ResourceContext); /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component manager. /// The reference to the context of the host. - private ResourceManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; - - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; - - _componentManager.ModuleManager.AddModule += (sender, moduleContext) => - { - AssignToModule(moduleContext); - }; + _componentHub = componentHub; - _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => - { - DetachFromModule(moduleContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentManager.SitemapManager.Register + _componentHub.EndpointManager.Register ( new EndpointRegistration() { - Factory = (resourceContext, uri, culture) => CreateResourceInstance(resourceContext as IResourceContext, culture), - ContextResolver = (type, moduleContext) => moduleContext != null ? GetResorces(type, moduleContext) : GetResorces(type), - EndpointResolver = () => Resources, - HandleRequest = (endpoint, endpointContext, request) => { return (endpoint as IResource).Process(request); } + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetResorces(type, applicationContext) : GetResorces(type), + EndpointsResolver = () => Resources, + HandleRequest = (request, endpointContext) => + { + var resourceContext = endpointContext as IResourceContext; + var resource = CreateResourceInstance(resourceContext, request.Culture) as IResource; + + return resource.Process(request); + } } ); @@ -90,48 +81,68 @@ private ResourceManager(IComponentHub componentManager, IHttpServerContext httpS } /// - /// Discovers and registers resources from the specified plugin. + /// Discovers and binds resources to an application. /// - /// A context of a plugin whose resources are to be registered. - public void Register(IPluginContext pluginContext) + /// The context of the plugin whose resources are to be associated. + private void Register(IPluginContext pluginContext) { if (_dictionary.ContainsKey(pluginContext)) { return; } - var assembly = pluginContext?.Assembly; + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } - _dictionary.Add(pluginContext, []); - var dict = _dictionary[pluginContext]; + /// + /// Discovers and binds resources to an application. + /// + /// The context of the application whose resources are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; foreach (var resourceType in assembly.GetTypes() - .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.IsClass && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IResource).Name) != null) .Where(x => x.GetInterface(typeof(IStatusPage).Name) == null)) { var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); - var parent = default(string); + var parent = default(Type); var contextPath = string.Empty; var includeSubPaths = false; - var moduleId = string.Empty; var conditions = new List(); - var optional = false; var cache = false; foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { - var buf = typeof(ModuleAttribute<>); - if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) { @@ -141,10 +152,6 @@ public void Register(IPluginContext pluginContext) { includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); } - else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) - { - moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -154,92 +161,50 @@ public void Register(IPluginContext pluginContext) { cache = true; } - else if (customAttribute.AttributeType == typeof(OptionalAttribute)) - { - optional = true; - } } - if (string.IsNullOrEmpty(moduleId)) + // assign the job to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { - // no module specified - _httpServerContext.Log.Warning - ( - I18N.Translate - ( - "webexpress:resourcemanager.moduleless", - id - ) - ); - - continue; - } - - if (!dict.ContainsKey(id)) - { - var resourceItem = new ResourceItem(_componentManager.ResourceManager) + var resourceContext = new ResourceContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) { - ResourceId = id, - ParentId = parent, + EndpointId = resourceType.FullName.ToLower(), + PluginContext = pluginContext, + ApplicationContext = applicationContext + }; + var resourceItem = new ResourceItem(_componentHub.ResourceManager) + { + ParentType = parent, ResourceClass = resourceType, - ModuleId = moduleId, + ResourceContext = resourceContext, Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, PathSegment = segment.ToPathSegment(), - Optional = optional, Log = _httpServerContext?.Log }; - resourceItem.AddResource += (s, e) => + if (_dictionary.AddResourceItem(pluginContext, applicationContext, resourceItem)) { - OnAddResource(e); - }; - - resourceItem.RemoveResource += (s, e) => - { - OnRemoveResource(e); - }; - - dict.Add(id, resourceItem); - - _httpServerContext?.Log.Debug - ( - I18N.Translate - ( - "webexpress:resourcemanager.addresource", - id, - moduleId - ) - ); - } - - // assign the resource to existing modules. - foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) - { - AssignToModule(moduleContext); + OnAddResource(resourceContext); + _httpServerContext?.Log.Debug( + I18N.Translate( + "webexpress:resourcemanager.addresource", + id, + applicationContext.ApplicationId + ) + ); + } } } } - /// - /// Discovers and registers resources from the specified plugin. - /// - /// A list with plugin contexts that contain the resources. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); - } - } - /// /// Removes all resources associated with the specified plugin context. /// /// The context of the plugin that contains the resources to remove. - public void Remove(IPluginContext pluginContext) + internal void Remove(IPluginContext pluginContext) { if (pluginContext == null) { @@ -252,8 +217,10 @@ public void Remove(IPluginContext pluginContext) return; } - foreach (var resourceItem in _dictionary[pluginContext].Values) + foreach (var resourceItem in _dictionary[pluginContext].Values + .SelectMany(x => x.Values)) { + OnRemoveResource(resourceItem.ResourceContext); resourceItem.Dispose(); } @@ -261,31 +228,28 @@ public void Remove(IPluginContext pluginContext) } /// - /// Assign existing resources to the module. + /// Removes all jobs associated with the specified application context. /// - /// The context of the module. - private void AssignToModule(IModuleContext moduleContext) + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + if (applicationContext == null) { - resourceItem.AddModule(moduleContext); + return; } - } - /// - /// Remove an existing modules to the application. - /// - /// The context of the module. - private void DetachFromModule(IModuleContext moduleContext) - { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + foreach (var pluginDict in _dictionary.Values) { - resourceItem.DetachModule(moduleContext); + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var resourceItem in appDict.Values) + { + OnRemoveResource(resourceItem.ResourceContext); + resourceItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); } } @@ -296,12 +260,14 @@ private void DetachFromModule(IModuleContext moduleContext) /// An enumeration of resource items. private IEnumerable GetResorceItems(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value); } - return _dictionary[pluginContext].Values; + return []; } /// @@ -311,13 +277,14 @@ private IEnumerable GetResorceItems(IPluginContext pluginContext) /// An enumeration of resource contexts. public IEnumerable GetResorces(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value.ResourceContext); } - return _dictionary[pluginContext].Values - .SelectMany(x => x.ResourceContexts); + return []; } /// @@ -339,7 +306,7 @@ public IEnumerable GetResorces(Type resourceType) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) + .SelectMany(x => x.Values) .Where(x => x.ResourceClass.Equals(resourceType)) .Select(x => x.ResourceContext); } @@ -348,15 +315,15 @@ public IEnumerable GetResorces(Type resourceType) /// Returns an enumeration of resource contextes. /// /// The resource type. - /// The context of the module. + /// The context of the application. /// An enumeration of resource contextes. - public IEnumerable GetResorces(Type resourceType, IModuleContext moduleContext) + public IEnumerable GetResorces(Type resourceType, IApplicationContext applicationContext) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) - .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) .Where(x => x.ResourceClass.Equals(resourceType)) + .Where(x => x.ResourceContext.ApplicationContext.Equals(applicationContext)) .Select(x => x.ResourceContext); } @@ -364,52 +331,32 @@ public IEnumerable GetResorces(Type resourceType, IModuleConte /// Returns an enumeration of resource contextes. /// /// The resource type. - /// The context of the module. + /// The context of the application. /// An enumeration of resource contextes. - public IEnumerable GetResorces(IModuleContext moduleContext) where T : IResource + public IEnumerable GetResorces(IApplicationContext applicationContext) where T : IResource { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) - .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) .Where(x => x.ResourceClass.Equals(typeof(T))) + .Where(x => x.ResourceContext.ApplicationContext.Equals(applicationContext)) .Select(x => x.ResourceContext); } - /// - /// Returns the resource context. - /// - /// The context of the module. - /// The resource type. - /// An resource context or null. - public IResourceContext GetResorce(IModuleContext moduleContext, Type resourceType) - { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) - .Where(x => x.ResourceContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.ResourceContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceClass.Equals(resourceType)) - .Select(x => x.ResourceContext) - .FirstOrDefault(); - } /// /// Returns the resource context. /// - /// The context of the module. + /// The context of the application. /// The resource id. /// An resource context or null. - public IResourceContext GetResorce(IModuleContext moduleContext, string resourceId) + public IResourceContext GetResorce(IApplicationContext applicationContext, string resourceId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts, (x, y) => new { x.ResourceClass, ResourceContext = y }) - .Where(x => x.ResourceContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.ResourceContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ResourceContext.EndpointId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) + .Where(x => x.ResourceContext.ApplicationContext.Equals(applicationContext)) + .Where(x => x.ResourceContext.EndpointId.Equals(resourceId)) .Select(x => x.ResourceContext) .FirstOrDefault(); } @@ -418,18 +365,16 @@ public IResourceContext GetResorce(IModuleContext moduleContext, string resource /// Returns the resource context. /// /// The application id. - /// The module id. /// The resource id. /// An resource context or null. - public IResourceContext GetResorce(string applicationId, string moduleId, string resourceId) + public IResourceContext GetResorce(string applicationId, string resourceId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.ResourceContexts) - .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) - .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.EndpointId.Equals(resourceId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) + .Where(x => x.ResourceContext.ApplicationContext.ApplicationId.Equals(applicationId)) + .Where(x => x.ResourceContext.EndpointId.Equals(resourceId)) + .Select(x => x.ResourceContext) .FirstOrDefault(); } @@ -443,11 +388,12 @@ private IResource CreateResourceInstance(IResourceContext resourceContext, Cultu { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) - .FirstOrDefault(x => x.ResourceContexts.Contains(resourceContext)); + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.ResourceContext.Equals(resourceContext)); if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _componentManager); + var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _componentHub); if (instance is II18N i18n) { @@ -483,6 +429,45 @@ private void OnRemoveResource(IResourceContext resourceContext) RemoveResource?.Invoke(this, resourceContext); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Information about the component is collected and prepared for output in the log. /// @@ -499,11 +484,22 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in I18N.Translate ( "webexpress:resourcemanager.resource", - resourcenItem.ResourceId, - string.Join(",", resourcenItem.ModuleId) + resourcenItem?.ResourceContext?.EndpointId, + string.Join(",", resourcenItem.ResourceContext?.ApplicationContext?.ApplicationId) ) ); } } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs index 881c4b8..97684e1 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs @@ -1,4 +1,4 @@ -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebRestApi diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs index 359a1f2..5960b70 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiContext.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebRestApi { diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs index ab9b335..79f079c 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApiManager.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebRestApi @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebRestApi /// /// The page manager manages rest api resources, which can be called with a URI (Uniform Resource Identifier). /// - public interface IRestApiManager : IEndpointManager + public interface IRestApiManager : IComponentManager { /// /// An event that fires when an rest api is added. @@ -51,41 +51,32 @@ public interface IRestApiManager : IEndpointManager /// Returns an enumeration of rest api contextes. /// /// The rest api type. - /// The context of the module. + /// The context of the application. /// An enumeration of rest api contextes. - IEnumerable GetRestApi(Type restApiType, IModuleContext moduleContext); + IEnumerable GetRestApi(Type restApiType, IApplicationContext applicationContext); /// /// Returns an enumeration of rest api contextes. /// /// The rest api type. - /// The context of the module. + /// The context of the application. /// An enumeration of rest api contextes. - IEnumerable GetRestApi(IModuleContext moduleContext) where T : IRestApi; + IEnumerable GetRestApi(IApplicationContext applicationContext) where T : IRestApi; /// /// Returns the rest api context. /// - /// The context of the module. + /// The context of the application. /// The rest api id. /// An rest api context or null. - IRestApiContext GetRestApi(IModuleContext moduleContext, string restApiId); - - /// - /// Returns the rest api context. - /// - /// The context of the module. - /// The rest api type. - /// An rest api context or null. - IRestApiContext GetRestApi(IModuleContext moduleContext, Type restApiType); + IRestApiContext GetRestApi(IApplicationContext applicationContext, string restApiId); /// /// Returns the rest api context. /// /// The application id. - /// The module id. /// The rest api id. /// An rest api context or null. - IRestApiContext GetRestApi(string applicationId, string moduleId, string restApiId); + IRestApiContext GetRestApi(string applicationId, string restApiId); } } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs index 2e92c21..5e09550 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs @@ -1,13 +1,145 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebRestApi.Model { /// + /// Represents a dictionary that maps plugin contexts to application contexts, rest api types, and rest api items. /// key = plugin context - /// value = { key = resource id, value = ressource item } + /// value = { key = rest api id, value = rest api item } /// - internal class RestApiDictionary : Dictionary> + internal class RestApiDictionary : Dictionary>> { + /// + /// Adds a rest api item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The rest api item. + /// True if the rest api item was added successfully, false if an element with the same status code already exists. + public bool AddRestApiItem(IPluginContext pluginContext, IApplicationContext applicationContext, RestApiItem restApiItem) + { + var type = restApiItem.RestApiClass; + + if (!typeof(IRestApi).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = new Dictionary>(); + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = new Dictionary(); + } + + var restApiDict = appContextDict[applicationContext]; + + if (!restApiDict.ContainsKey(type)) + { + restApiDict[type] = restApiItem; + return true; + } + + return false; // item with the same rest api class already exists + } + + /// + /// Removes a rest api from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemoveRestApi(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IRestApi + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var restApiDict = appContextDict[applicationContext]; + + if (restApiDict.ContainsKey(type)) + { + restApiDict.Remove(type); + + if (restApiDict.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the rest api items from the dictionary. + /// + /// The type of rest api. + /// The application context. + /// An IEnumerable of rest api items + public IEnumerable GetRestApiItems(IApplicationContext applicationContext) where T : IRestApi + { + return GetRestApiItems(applicationContext, typeof(T)); + } + + /// + /// Returns the rest api items from the dictionary. + /// + /// The application context. + /// The type of rest api. + /// An IEnumerable of rest api items + public IEnumerable GetRestApiItems(IApplicationContext applicationContext, Type restApiType) + { + if (!typeof(IRestApi).IsAssignableFrom(restApiType)) + { + return Enumerable.Empty(); + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var restApiDict = appContextDict[applicationContext]; + + if (restApiDict.ContainsKey(restApiType)) + { + return new List { restApiDict[restApiType] }; + } + } + } + + return Enumerable.Empty(); + } + + /// + /// Returns all rest api contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of rest api contexts. + public IEnumerable GetRestApis(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Values) + .SelectMany(dict => dict.Values) + .Select(x => x.ContextPath as IRestApiContext); + } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs index ac931be..fa6f5ad 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi.Model @@ -18,24 +14,9 @@ internal class RestApiItem : IDisposable private readonly IRestApiManager _restApiManager; /// - /// An event that fires when an rest api resource is added. + /// Returns or sets the parent type. /// - public event EventHandler AddRestApi; - - /// - /// An event that fires when an rest api resource is removed. - /// - public event EventHandler RemoveRestApi; - - /// - /// Returns or sets the rest api resource id. - /// - public string RestApiId { get; set; } - - /// - /// Returns or sets the parent id. - /// - public string ParentId { get; set; } + public Type ParentType { get; set; } /// /// Returns or sets the type of rest api resource. @@ -47,11 +28,6 @@ internal class RestApiItem : IDisposable /// public IRestApi Instance { get; set; } - /// - /// Returns or sets the module id. - /// - public string ModuleId { get; set; } - /// /// Returns or sets the paths of the resource. /// @@ -97,21 +73,10 @@ internal class RestApiItem : IDisposable /// public ILog Log { get; internal set; } - /// - /// Returns the directory where the module instances are listed. - /// - private IDictionary Dictionary { get; } - = new Dictionary(); - - /// - /// Returns the associated module contexts. - /// - public IEnumerable ModuleContexts => Dictionary.Keys; - /// /// Returns the rest api contexts. /// - public IEnumerable RestApiContexts => Dictionary.Values; + public IRestApiContext RestApiContext { get; internal set; } /// /// Initializes a new instance of the class. @@ -122,106 +87,11 @@ internal RestApiItem(IRestApiManager restApiManager) _restApiManager = restApiManager; } - /// - /// Adds an module assignment - /// - /// The context of the module. - public void AddModule(IModuleContext moduleContext) - { - // only if no instance has been created yet - if (Dictionary.ContainsKey(moduleContext)) - { - Log.Warning(message: I18N.Translate("webexpress:restapimanager.addresource.duplicate", RestApiId, moduleContext.ModuleId)); - - return; - } - - // create context - var restApiContext = new RestApiContext(moduleContext, _restApiManager, this) - { - Conditions = Conditions, - EndpointId = RestApiId, - Cache = Cache, - IncludeSubPaths = IncludeSubPaths, - Methods = Methods, - Version = Version - }; - - if - ( - !Optional || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.{RestApiId.ToLower()}") || - moduleContext.ApplicationContext.Options.Contains($"{ModuleId.ToLower()}.*") || - moduleContext.ApplicationContext.Options.Where(x => Regex.Match($"{ModuleId.ToLower()}.{RestApiId.ToLower()}", x).Success).Any() - ) - { - Dictionary.Add(moduleContext, restApiContext); - OnAddRestApi(restApiContext); - } - } - - /// - /// Remove an module assignment. - /// - /// The context of the module. - public void DetachModule(IModuleContext moduleContext) - { - // not an assignment has been created yet - if (!Dictionary.ContainsKey(moduleContext)) - { - return; - } - - foreach (var restApiContext in Dictionary.Values) - { - OnRemovePage(restApiContext); - } - - Dictionary.Remove(moduleContext); - } - - /// - /// Checks whether a module context is already assigned to the item. - /// - /// The module context. - /// True a mapping exists, false otherwise. - public bool IsAssociatedWithModule(IModuleContext moduleContext) - { - return Dictionary.ContainsKey(moduleContext); - } - - /// - /// Raises the AddRestApi event. - /// - /// The rest api context. - private void OnAddRestApi(IRestApiContext restApiContext) - { - AddRestApi?.Invoke(this, restApiContext); - } - - /// - /// Raises the RemoveRestApi event. - /// - /// The rest api context. - private void OnRemovePage(IRestApiContext restApiContext) - { - RemoveRestApi?.Invoke(this, restApiContext); - } - /// /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged rest api resources. /// public void Dispose() { - foreach (Delegate d in AddRestApi.GetInvocationList()) - { - AddRestApi -= (EventHandler)d; - } - - foreach (Delegate d in RemoveRestApi.GetInvocationList()) - { - RemoveRestApi -= (EventHandler)d; - } } /// @@ -230,7 +100,7 @@ public void Dispose() /// The rest api resource element in its string representation. public override string ToString() { - return $"RestApi '{RestApiId}'"; + return $"RestApi: '{RestApiContext?.EndpointId}'"; } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs index 6a70d5c..211109a 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebModule; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebRestApi.Model; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi @@ -15,25 +14,20 @@ namespace WebExpress.WebCore.WebRestApi /// public class RestApiContext : IRestApiContext { - private readonly IRestApiManager _restApiManager; - private readonly RestApiItem _restApiItem; + private readonly IEndpointManager _endpointManager; + private readonly Type _parentType; + private readonly UriResource _contextPath; + private readonly IUriPathSegment _pathSegment; /// /// Returns the associated plugin context. /// - public IPluginContext PluginContext { get; private set; } + public IPluginContext PluginContext { get; internal set; } /// - /// Returns the corresponding module context. + /// Returns the corresponding application context. /// - public IModuleContext ModuleContext { get; private set; } - - /// - /// Returns the scope names that provides the resource. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - public IEnumerable Scopes { get; internal set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns the conditions that must be met for the resource to be active. @@ -53,10 +47,7 @@ public class RestApiContext : IRestApiContext /// /// Returns the parent or null if not used. /// - public IEndpointContext ParentContext => _restApiManager.RestApis - .Where(x => !string.IsNullOrWhiteSpace(_restApiItem.ParentId)) - .Where(x => x.EndpointId.Equals(_restApiItem.ParentId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ApplicationContext == ModuleContext.ApplicationContext) + public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) .FirstOrDefault(); /// @@ -84,30 +75,31 @@ public UriResource ContextPath var parentContext = ParentContext; if (parentContext != null) { - return UriResource.Combine(ParentContext?.Uri, _restApiItem.ContextPath, Version.ToString()); + return UriResource.Combine(ParentContext?.Uri, _contextPath, Version.ToString()); } - return UriResource.Combine(ModuleContext.ContextPath, _restApiItem.ContextPath, Version.ToString()); + return UriResource.Combine(ApplicationContext?.ContextPath, _contextPath, Version.ToString()); } } /// /// Returns the uri. /// - public UriResource Uri => ContextPath.Append(_restApiItem.PathSegment); + public UriResource Uri => ContextPath.Append(_pathSegment); /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified parent type and context path. /// - /// The module context. - /// The resource manager. - /// The page item or null. - internal RestApiContext(IModuleContext moduleContext, IRestApiManager restApiManager, RestApiItem restApiItem = null) + /// The endpoint manager responsible for managing endpoints. + /// The type of the parent resource. + /// The context path of the resource. + /// The path segment of the resource. + public RestApiContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) { - PluginContext = moduleContext?.PluginContext; - ModuleContext = moduleContext; - _restApiManager = restApiManager; - _restApiItem = restApiItem; + _endpointManager = endpointManager; + _parentType = parentType; + _contextPath = contextPath; + _pathSegment = pathSegment; } /// @@ -116,7 +108,7 @@ internal RestApiContext(IModuleContext moduleContext, IRestApiManager restApiMan /// A string that represents the current object. public override string ToString() { - return $"{ModuleContext?.ApplicationContext?.ApplicationId}:{ModuleContext?.ModuleId}:{EndpointId}"; + return $"RestApi: {EndpointId}"; } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 688a494..b41f018 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -5,14 +5,14 @@ using System.Text; using System.Text.Json; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebRestApi.Model; -using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi @@ -22,7 +22,7 @@ namespace WebExpress.WebCore.WebRestApi /// public class RestApiManager : IRestApiManager { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly RestApiDictionary _dictionary = []; private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true }; @@ -42,48 +42,33 @@ public class RestApiManager : IRestApiManager /// public IEnumerable RestApis => _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts); + .SelectMany(x => x.Values) + .Select(x => x.RestApiContext); /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private RestApiManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; - - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; - - _componentManager.ModuleManager.AddModule += (sender, moduleContext) => - { - AssignToModule(moduleContext); - }; + _componentHub = componentHub; - _componentManager.ModuleManager.RemoveModule += (sender, moduleContext) => - { - DetachFromModule(moduleContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentManager.SitemapManager.Register + _componentHub.EndpointManager.Register ( new EndpointRegistration() { - Factory = (resourceContext, uri, culture) => CreatePageInstance(resourceContext as IRestApiContext, culture), - ContextResolver = (type, moduleContext) => moduleContext != null ? GetRestApi(type, moduleContext) : GetRestApi(type), - EndpointResolver = () => RestApis, - HandleRequest = (endpoint, endpontContext, request) => + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetRestApi(type, applicationContext) : GetRestApi(type), + EndpointsResolver = () => RestApis, + HandleRequest = (request, endpointContext) => { - var restApi = endpoint as IRestApi; - var restApiContext = endpontContext as IRestApiContext; + var restApiContext = endpointContext as IRestApiContext; + var restApi = CreatePageInstance(restApiContext, request.Culture) as IRestApi; if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) { @@ -141,13 +126,14 @@ private RestApiManager(IComponentHub componentManager, IHttpServerContext httpSe /// An enumeration of rest api resource contexts. public IEnumerable GetRestApi(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value.RestApiContext); } - return _dictionary[pluginContext].Values - .SelectMany(x => x.RestApiContexts); + return []; } /// @@ -169,7 +155,7 @@ public IEnumerable GetRestApi(Type restApiType) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) + .SelectMany(x => x.Values) .Where(x => x.RestApiClass.Equals(restApiType)) .Select(x => x.RestApiContext); } @@ -178,15 +164,15 @@ public IEnumerable GetRestApi(Type restApiType) /// Returns an enumeration of rest api resource contextes. /// /// The page type. - /// The context of the module. + /// The context of the application. /// An enumeration of page contextes. - public IEnumerable GetRestApi(Type restApiType, IModuleContext moduleContext) + public IEnumerable GetRestApi(Type restApiType, IApplicationContext applicationContext) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) - .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) .Where(x => x.RestApiClass.Equals(restApiType)) + .Where(x => x.RestApiContext.ApplicationContext.Equals(applicationContext)) .Select(x => x.RestApiContext); } @@ -194,52 +180,31 @@ public IEnumerable GetRestApi(Type restApiType, IModuleContext /// Returns an enumeration of rest api resource contextes. /// /// The rest api resource type. - /// The context of the module. + /// The context of the application. /// An enumeration of rest api resource contextes. - public IEnumerable GetRestApi(IModuleContext moduleContext) where T : IRestApi + public IEnumerable GetRestApi(IApplicationContext applicationContext) where T : IRestApi { return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) - .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.RestApiClass.Equals(typeof(T))) - .Select(x => x.RestApiContext); + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.RestApiClass.Equals(typeof(T))) + .Where(x => x.RestApiContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.RestApiContext); } /// /// Returns the rest api resource context. /// - /// The context of the module. + /// The context of the application. /// The rest api resource id. /// An rest api resource context or null. - public IRestApiContext GetRestApi(IModuleContext moduleContext, string restApiId) + public IRestApiContext GetRestApi(IApplicationContext applicationContext, string restApiId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) - .Where(x => x.RestApiContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.RestApiContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.RestApiContext.EndpointId.Equals(restApiId, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.RestApiContext) - .FirstOrDefault(); - } - - /// - /// Returns the rest api resource context. - /// - /// The context of the module. - /// The rest api resource type. - /// An rest api resource context or null. - public IRestApiContext GetRestApi(IModuleContext moduleContext, Type restApiType) - { - return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts, (x, y) => new { x.RestApiClass, RestApiContext = y }) - .Where(x => x.RestApiContext.ModuleContext?.ApplicationContext != null) - .Where(x => x.RestApiContext.ModuleContext.ApplicationContext.ApplicationId.Equals(moduleContext.ApplicationContext.ApplicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.RestApiContext.ModuleContext.ModuleId.Equals(moduleContext.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.RestApiClass.Equals(restApiType)) + .Where(x => x.RestApiContext.ApplicationContext.Equals(applicationContext)) + .Where(x => x.RestApiContext.EndpointId.Equals(restApiId)) .Select(x => x.RestApiContext) .FirstOrDefault(); } @@ -248,18 +213,16 @@ public IRestApiContext GetRestApi(IModuleContext moduleContext, Type restApiType /// Returns the rest api resource context. /// /// The application id. - /// The module id. /// The rest api resource id. /// An rest api resource context or null. - public IRestApiContext GetRestApi(string applicationId, string moduleId, string restApiId) + public IRestApiContext GetRestApi(string applicationId, string restApiId) { return _dictionary.Values .SelectMany(x => x.Values) - .SelectMany(x => x.RestApiContexts) - .Where(x => x.ModuleContext != null && x.ModuleContext.ApplicationContext != null) - .Where(x => x.ModuleContext.ApplicationContext.ApplicationId.Equals(applicationId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.ModuleContext.ModuleId.Equals(moduleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => x.EndpointId.Equals(restApiId, StringComparison.OrdinalIgnoreCase)) + .SelectMany(x => x.Values) + .Where(x => x.RestApiContext.ApplicationContext.ApplicationId.Equals(applicationId)) + .Where(x => x.RestApiContext.EndpointId.Equals(restApiId)) + .Select(x => x.RestApiContext) .FirstOrDefault(); } @@ -273,11 +236,12 @@ private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo cul { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) - .FirstOrDefault(x => x.RestApiContexts.Contains(pageContext)); + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.RestApiContext.Equals(pageContext)); if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _componentManager); + var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _componentHub); if (instance is II18N i18n) { @@ -296,20 +260,44 @@ private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo cul } /// - /// Discovers and registers rest api resources from the specified plugin. + /// Discovers and binds rest apis to an application. /// - /// A context of a plugin whose rest api resources are to be registered. - public void Register(IPluginContext pluginContext) + /// The context of the plugin whose rest apis are to be associated. + private void Register(IPluginContext pluginContext) { if (_dictionary.ContainsKey(pluginContext)) { return; } - var assembly = pluginContext?.Assembly; + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } - _dictionary.Add(pluginContext, []); - var dict = _dictionary[pluginContext]; + /// + /// Discovers and binds rest apis to an application. + /// + /// The context of the application whose rest apis are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers rest apis for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; foreach (var resourceType in assembly.GetTypes() .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) @@ -318,12 +306,10 @@ public void Register(IPluginContext pluginContext) var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); var title = resourceType.Name; - var parent = default(string); + var parent = default(Type); var contextPath = string.Empty; var includeSubPaths = false; - var moduleId = string.Empty; var conditions = new List(); - var optional = false; var cache = false; var methods = new List(); var version = 1u; @@ -331,15 +317,13 @@ public void Register(IPluginContext pluginContext) foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { - var buf = typeof(ModuleAttribute<>); - if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) { @@ -349,10 +333,6 @@ public void Register(IPluginContext pluginContext) { includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); } - else if (customAttribute.AttributeType.Name == typeof(ModuleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ModuleAttribute<>).Namespace) - { - moduleId = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower(); - } else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -367,10 +347,6 @@ public void Register(IPluginContext pluginContext) { cache = true; } - else if (customAttribute.AttributeType == typeof(OptionalAttribute)) - { - optional = true; - } } foreach (var customAttribute in resourceType.CustomAttributes @@ -383,29 +359,26 @@ public void Register(IPluginContext pluginContext) } - if (string.IsNullOrEmpty(moduleId)) + // assign the job to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { - // no module specified - _httpServerContext.Log.Warning - ( - I18N.Translate - ( - "webexpress:restapimanager.moduleless", - id - ) - ); - - continue; - } + var restApiContext = new RestApiContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) + { + EndpointId = resourceType.FullName.ToLower(), + PluginContext = pluginContext, + ApplicationContext = applicationContext, + Cache = cache, + Conditions = conditions, + Methods = methods, + Version = version, + IncludeSubPaths = includeSubPaths + }; - if (!dict.ContainsKey(id)) - { - var restApiItem = new RestApiItem(_componentManager.RestApiManager) + var restApiItem = new RestApiItem(_componentHub.RestApiManager) { - RestApiId = id, - ParentId = parent, + ParentType = parent, + RestApiContext = restApiContext, RestApiClass = resourceType, - ModuleId = moduleId, Methods = methods.Distinct(), Version = version, Cache = cache, @@ -413,53 +386,27 @@ public void Register(IPluginContext pluginContext) ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, PathSegment = segment.ToPathSegment(), - Optional = optional, Log = _httpServerContext?.Log }; - restApiItem.AddRestApi += (s, e) => + if (_dictionary.AddRestApiItem(pluginContext, applicationContext, restApiItem)) { - OnAddRestApi(e); - }; + OnAddRestApi(restApiContext); - restApiItem.RemoveRestApi += (s, e) => - { - OnRemoveRestApi(e); - }; - - dict.Add(id, restApiItem); - - _httpServerContext?.Log.Debug - ( - I18N.Translate + _httpServerContext?.Log.Debug ( - "webexpress:restapimanager.addresource", - id, - moduleId - ) - ); - } - - // assign the rest api resource to existing modules. - foreach (var moduleContext in _componentManager.ModuleManager.GetModules(pluginContext, moduleId)) - { - AssignToModule(moduleContext); + I18N.Translate + ( + "webexpress:restapimanager.addresource", + id, + applicationContext.ApplicationId + ) + ); + } } } } - /// - /// Discovers and registers rest api resource from the specified plugin. - /// - /// A list with plugin contexts that contain the resources. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); - } - } - /// /// Removes all pages associated with the specified plugin context. /// @@ -477,8 +424,10 @@ public void Remove(IPluginContext pluginContext) return; } - foreach (var resourceItem in _dictionary[pluginContext].Values) + foreach (var resourceItem in _dictionary[pluginContext].Values + .SelectMany(x => x.Values)) { + OnRemoveRestApi(resourceItem.RestApiContext); resourceItem.Dispose(); } @@ -486,31 +435,28 @@ public void Remove(IPluginContext pluginContext) } /// - /// Assign existing rest api resource to the module. + /// Removes all jobs associated with the specified application context. /// - /// The context of the module. - private void AssignToModule(IModuleContext moduleContext) + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => x.ModuleId.Equals(moduleContext?.ModuleId, StringComparison.OrdinalIgnoreCase)) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + if (applicationContext == null) { - resourceItem.AddModule(moduleContext); + return; } - } - /// - /// Remove an existing modules to the application. - /// - /// The context of the module. - private void DetachFromModule(IModuleContext moduleContext) - { - foreach (var resourceItem in _dictionary.Values - .SelectMany(x => x.Values) - .Where(x => !x.IsAssociatedWithModule(moduleContext))) + foreach (var pluginDict in _dictionary.Values) { - resourceItem.DetachModule(moduleContext); + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var resourceItem in appDict.Values) + { + OnRemoveRestApi(resourceItem.RestApiContext); + resourceItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); } } @@ -519,14 +465,16 @@ private void DetachFromModule(IModuleContext moduleContext) /// /// A context of a plugin whose rest api resources are to be registered. /// An enumeration of rest api resource items. - private IEnumerable GetPageItems(IPluginContext pluginContext) + private IEnumerable GetRestApiItems(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) { - return []; + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value); } - return _dictionary[pluginContext].Values; + return []; } /// @@ -547,6 +495,46 @@ private void OnRemoveRestApi(IRestApiContext pageContext) RemoveRestApi?.Invoke(this, pageContext); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Collects and prepares information about the component for output in the log. /// @@ -555,7 +543,7 @@ private void OnRemoveRestApi(IRestApiContext pageContext) /// The depth of the log. public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { - foreach (var resourcenItem in GetPageItems(pluginContext)) + foreach (var resourcenItem in GetRestApiItems(pluginContext)) { output.Add ( @@ -563,11 +551,22 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in I18N.Translate ( "webexpress:restapimanager.resource", - resourcenItem.RestApiId, - string.Join(",", resourcenItem.ModuleId) + resourcenItem?.RestApiContext?.EndpointId, + string.Join(",", resourcenItem?.RestApiContext?.ApplicationContext?.ApplicationId) ) ); } } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + } } } diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index 0bde8e6..c862719 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -84,5 +84,12 @@ public Session GetSession(Request request) public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs index 30a1630..c65a63b 100644 --- a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -1,8 +1,9 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap @@ -17,19 +18,6 @@ public interface ISitemapManager : IComponentManager /// IEnumerable SiteMap { get; } - /// - /// Registers an endpoint manager. - /// - /// The type of the endpoint context. - /// The registration details containing the callback functions. - void Register(EndpointRegistration registration) where T : IEndpointContext; - - /// - /// Removes the registration for a specific endpoint manager. - /// - /// The type of the endpoint context. - void Remove() where T : IEndpointContext; - /// /// Rebuilds the sitemap. /// @@ -63,15 +51,15 @@ public interface ISitemapManager : IComponentManager /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// The module context. + /// The application context. /// Returns the uri taking into account the context or null. - UriResource GetUri(IModuleContext moduleContext) where T : IEndpoint; + UriResource GetUri(IApplicationContext applicationContext) where T : IEndpoint; /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// The module context. + /// The endpoint context. /// Returns the uri taking into account the context or null. UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint; } diff --git a/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs index b519a4e..600f98d 100644 --- a/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSitemap.Model diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index 22e6eb4..c477dfa 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebMessage; +using System.Collections.Generic; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebSitemap.Model; using WebExpress.WebCore.WebUri; @@ -12,23 +10,11 @@ namespace WebExpress.WebCore.WebSitemap /// public class SearchResult { - private readonly Func _handleRequest; - - /// - /// Returns the endpoint id. - /// - public string EndpointId { get; internal set; } - /// /// Returns the context of the endpoint. /// public IEndpointContext EndpointContext { get; internal set; } - /// - /// Returns the instance. - /// - public IEndpoint Instance { get; internal set; } - /// /// Returns the search context. /// @@ -50,29 +36,5 @@ public class SearchResult /// /// The uri. public UriResource Uri { get; internal set; } - - /// - /// Initializes a new instance of the class. - /// - /// The function to handle requests. - internal SearchResult(Func handleRequest) - { - _handleRequest = handleRequest; - } - - /// - /// Processing of the resource. - /// - /// The request. - /// The response. - public Response Process(Request request) - { - if (_handleRequest != null) - { - return _handleRequest(Instance, EndpointContext, request); - } - - return new ResponseBadRequest(); - } } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 5b03b50..72ce28a 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -4,9 +4,9 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebModule; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSitemap.Model; using WebExpress.WebCore.WebUri; @@ -19,8 +19,7 @@ namespace WebExpress.WebCore.WebSitemap public sealed class SitemapManager : ISitemapManager, ISystemComponent { private SitemapNode _root = new(); - private readonly Dictionary _registrations = []; - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; /// @@ -33,11 +32,11 @@ public sealed class SitemapManager : ISitemapManager, ISystemComponent /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private SitemapManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private SitemapManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentHub; _httpServerContext = httpServerContext; _httpServerContext.Log.Debug @@ -46,30 +45,6 @@ private SitemapManager(IComponentHub componentManager, IHttpServerContext httpSe ); } - /// - /// Registers an endpoint manager. - /// - /// The type of the endpoint manager. - /// The registration details containing the callback functions. - public void Register(EndpointRegistration registration) where T : IEndpointContext - { - var type = typeof(T); - if (!_registrations.ContainsKey(type)) - { - _registrations[type] = registration; - } - } - - /// - /// Removes the registration for a specific endpoint manager. - /// - /// The type of the endpoint manager. - public void Remove() where T : IEndpointContext - { - var type = typeof(T); - _registrations.Remove(type); - } - /// /// Rebuilds the sitemap. /// @@ -83,7 +58,7 @@ public void Refresh() ); // applications - var applications = _componentManager.ApplicationManager.Applications + var applications = _componentHub.ApplicationManager.Applications .Select(x => new { ApplicationContext = x, @@ -100,26 +75,8 @@ public void Refresh() )); } - // modules - var modules = _componentManager.ModuleManager.Modules - .Select(x => new - { - ModuleContext = x, - x.ContextPath.PathSegments - }) - .OrderBy(x => x.PathSegments.Count); - - foreach (var module in modules) - { - MergeSitemap(newSiteMapNode, CreateSiteMap - ( - new Queue(module.PathSegments), - module.ModuleContext - )); - } - - // resourcen - var resources = _registrations.Values.SelectMany(x => x.EndpointResolver()) + // endpoints + var resources = _componentHub.EndpointManager.Endpoints .Select(x => new { EndpointContext = x, @@ -181,7 +138,7 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) /// Returns the uri taking into account the context or null. public UriResource GetUri(params Parameter[] parameters) where T : IEndpoint { - var endpointContexts = ResolveEndpointContexts(typeof(T)); + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T)); var node = _root.GetPreOrder() .Where(x => endpointContexts.Contains(x.EndpointContext)) @@ -198,7 +155,7 @@ public UriResource GetUri(params Parameter[] parameters) where T : IEndpoint /// Returns the URI taking into account the context, or null if no valid URI is found. public UriResource GetUri(Type resourceType, params Parameter[] parameters) { - var endpointContexts = ResolveEndpointContexts(resourceType); + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(resourceType); var node = _root.GetPreOrder() .Where(x => endpointContexts.Contains(x.EndpointContext)) @@ -211,11 +168,11 @@ public UriResource GetUri(Type resourceType, params Parameter[] parameters) /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// The module context. + /// The application context. /// Returns the uri taking into account the context or null. - public UriResource GetUri(IModuleContext moduleContext) where T : IEndpoint + public UriResource GetUri(IApplicationContext applicationContext) where T : IEndpoint { - var endpointContexts = ResolveEndpointContexts(typeof(T), moduleContext); + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T), applicationContext); var node = _root.GetPreOrder() .Where(x => endpointContexts.Contains(x.EndpointContext)) @@ -232,7 +189,7 @@ public UriResource GetUri(IModuleContext moduleContext) where T : IEndpoint /// Returns the uri taking into account the context or null. public UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint { - var endpointContexts = ResolveEndpointContexts(typeof(T), endpointContext.ModuleContext) + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T), endpointContext.ApplicationContext) .Where(x => x.EndpointId.Equals(endpointContext.EndpointId, StringComparison.OrdinalIgnoreCase)); var node = _root.GetPreOrder() @@ -305,68 +262,6 @@ SitemapNode parent return node; } - /// - /// Creates the sitemap. Works recursively. - /// It is important for the algorithm that the addition of module is sorted - /// by the number of path segments in ascending order. - /// - /// The path segments of the context path. - /// The module context. - /// The sitemap root node. - private static SitemapNode CreateSiteMap - ( - Queue contextPathSegments, - IModuleContext moduleContext - ) - { - var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; - var next = CreateSiteMap(contextPathSegments, moduleContext, root); - - if (next != null) - { - root.Children.Add(next); - } - - return root; - } - - /// - /// Creates the sitemap. Works recursively. - /// It is important for the algorithm that the addition of module is sorted - /// by the number of path segments in ascending order. - /// - /// The path segments of the context path. - /// The application context. - /// The parent node or null if root. - /// The sitemap root node. - private static SitemapNode CreateSiteMap - ( - Queue contextPathSegments, - IModuleContext moduleContext, - SitemapNode parent - ) - { - var pathSegment = contextPathSegments.Count != 0 ? contextPathSegments.Dequeue() : null; - - if (pathSegment == null) - { - return null; - } - - var node = new SitemapNode() - { - PathSegment = pathSegment as IUriPathSegment, - Parent = parent, - }; - - if (contextPathSegments.Any()) - { - node.Children.Add(CreateSiteMap(contextPathSegments, moduleContext, node)); - } - - return node; - } - /// /// Creates the sitemap. Works recursively. /// It is important for the algorithm that the addition of module is sorted @@ -488,35 +383,25 @@ SearchContext searchContext } var type = node.EndpointContext?.GetType(); - var registration = default(EndpointRegistration); - - if (type != null && _registrations.TryGetValue(type, out var _registration)) - { - registration = _registration; - } outPathSegments.Enqueue(copy); if (nextPathSegment == null) { - return new SearchResult(registration?.HandleRequest) + return new SearchResult() { - EndpointId = node.EndpointContext.EndpointId, EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource([.. outPathSegments]), - Instance = CreateEndpoint(node, new UriResource([.. outPathSegments]), searchContext) + Uri = new UriResource([.. outPathSegments]) }; } else if (node.IsLeaf && nextPathSegment != null && node.EndpointContext != null && node.EndpointContext.IncludeSubPaths) { - return new SearchResult(registration?.HandleRequest) + return new SearchResult() { - EndpointId = node.EndpointContext.EndpointId, EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource([.. outPathSegments]), - Instance = CreateEndpoint(node, new UriResource([.. outPathSegments]), searchContext) + Uri = new UriResource([.. outPathSegments]) }; } @@ -527,7 +412,7 @@ SearchContext searchContext } // 404 - return new SearchResult(null) + return new SearchResult() { EndpointContext = node.EndpointContext, SearchContext = searchContext, @@ -535,51 +420,6 @@ SearchContext searchContext }; } - /// - /// Creates a new instance or if caching is active, a possibly existing instance is returned. - /// - /// The sitemap node. - /// The uri. - /// The search context. - /// The created endpoint. - private IEndpoint CreateEndpoint(SitemapNode node, UriResource uri, SearchContext context) - { - if (node == null || node.EndpointContext == null) - { - return null; - } - - var type = node.EndpointContext.GetType(); - - if (_registrations.TryGetValue(type, out var registration)) - { - return registration.Factory(node.EndpointContext, uri, context.Culture); - } - - throw new InvalidOperationException($"No factory registered for type {type}"); - } - - /// - /// Resolves the endpoint context for the specified endpoint. - /// - /// The type of the endpoint. - /// The optional module context. - /// An enumerable of the corresponding endpoint contexts. - private IEnumerable ResolveEndpointContexts(Type endpointType, IModuleContext moduleContext = null) - { - foreach (var contextResolver in _registrations.Values.Select(x => x.ContextResolver)) - { - var res = contextResolver(endpointType, moduleContext); - - if (res.Any()) - { - return res; - } - } - - return []; - } - /// /// Checks whether the node matches the path element. /// @@ -635,5 +475,12 @@ public override string ToString() { return string.Join(" | ", _root.GetPreOrder()); } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs index 7aac0c6..d1c9b05 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageManager.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Management of status pages. /// - public interface IStatusPageManager : IComponent + public interface IStatusPageManager : IComponentManager { /// /// An event that fires when an status page is added. diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs index 5fbf3a1..c7c0317 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs @@ -113,9 +113,12 @@ public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, /// The status page item if found, otherwise null. public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, Type statusPageType) { - return Values.SelectMany(applicationDict => applicationDict.Values) - .SelectMany(statusDict => statusDict.Values) - .FirstOrDefault(statusPageItem => statusPageItem.StatusPageClass == statusPageType); + return Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .Select(x => x.Value) + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.StatusPageClass == statusPageType); } /// diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs index 78a0a6f..3473dde 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs @@ -38,5 +38,14 @@ public class StatusPageContext : IStatusPageContext /// Returns the status icon. /// public UriResource StatusIcon { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"StatusPage: {StatusId}"; + } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 697036b..8c621af 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -17,7 +17,7 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Management of status pages. /// - public class StatusPageManager : IStatusPageManager, IComponentManagerPlugin, ISystemComponent + public class StatusPageManager : IStatusPageManager, ISystemComponent { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; @@ -47,19 +47,14 @@ public class StatusPageManager : IStatusPageManager, IComponentManagerPlugin, IS /// /// The component manager. /// The reference to the context of the host. - internal StatusPageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private StatusPageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) { _componentHub = componentManager; - _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => - { - Register(pluginContext); - }; - - _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => - { - Remove(pluginContext); - }; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; _httpServerContext = httpServerContext; @@ -70,10 +65,42 @@ internal StatusPageManager(IComponentHub componentManager, IHttpServerContext ht } /// - /// Discovers and registers status pages from the specified plugin. + /// Discovers and binds status pages to an application. + /// + /// The context of the plugin whose status pages are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds status pages to an application. /// - /// A context of a plugin whose status pages are to be registered. - public void Register(IPluginContext pluginContext) + /// The context of the application whose status pages are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -82,7 +109,6 @@ public void Register(IPluginContext pluginContext) .Where(x => x.GetInterface(typeof(IStatusPage).Name) != null)) { var id = resource.FullName?.ToLower(); - var applicationIds = new List(); var statusResponse = typeof(ResponseInternalServerError); var icon = string.Empty; var title = resource.Name; @@ -95,10 +121,6 @@ public void Register(IPluginContext pluginContext) { statusResponse = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } - else if (customAttribute.AttributeType.Name == typeof(ApplicationAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ApplicationAttribute<>).Namespace) - { - applicationIds.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); - } else if (customAttribute.AttributeType == typeof(TitleAttribute)) { title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); @@ -113,31 +135,24 @@ public void Register(IPluginContext pluginContext) } } - if (!applicationIds.Any()) - { - // no application specified - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:statuspagemanager.applicationless", id) - ); - break; - } - if (applicationIds.Count() > 1) + // assign the status pages to existing applications. + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) { - // too many specified applications - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:statuspagemanager.applicationrich", id) - ); - } - - // assign the module to existing applications. - var applicationContext = _componentHub.ApplicationManager.GetApplication(applicationIds.FirstOrDefault()); + if (statusResponse?.GetCustomAttribute()?.StatusCode == null) + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:statuspagemanager.statuscodeless", + resource.Name, + applicationContext?.ApplicationId + ) + ); + } - if (statusResponse != default) - { var stausIcon = !string.IsNullOrEmpty(icon) ? UriResource.Combine(applicationContext.ContextPath, icon) : null; var statusCode = statusResponse.GetCustomAttribute().StatusCode; var statusPageContext = new StatusPageContext() @@ -226,18 +241,6 @@ public void Register(IPluginContext pluginContext) } } - else - { - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:statuspagemanager.statuscode", - resource.Name, - applicationIds.FirstOrDefault() - ) - ); - } } } @@ -245,7 +248,7 @@ public void Register(IPluginContext pluginContext) /// Discovers and registers entries from the specified plugin. /// /// A list with plugin contexts that contain the status pages. - public void Register(IEnumerable pluginContexts) + private void Register(IEnumerable pluginContexts) { foreach (var pluginContext in pluginContexts) { @@ -338,7 +341,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon /// Removes all status pages associated with the specified plugin context. /// /// The context of the plugin that contains the status pages to remove. - public void Remove(IPluginContext pluginContext) + internal void Remove(IPluginContext pluginContext) { if (pluginContext == null) { @@ -354,6 +357,32 @@ public void Remove(IPluginContext pluginContext) _dictionary.Remove(pluginContext); } + /// + /// Removes all jobs associated with the specified application context. + /// + /// The context of the application that contains the jobs to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + foreach (var pluginDict in _dictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var resourceItem in appDict.Values) + { + OnRemoveStatusPage(resourceItem.StatusPageContext); + resourceItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + } + /// /// Raises the AddStatusPage event. /// @@ -372,6 +401,45 @@ private void OnRemoveStatusPage(IStatusPageContext statusPage) RemoveStatusPage?.Invoke(this, statusPage); } + /// + /// Handles the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Handles the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + /// /// Information about the component is collected and prepared for output in the log. /// @@ -399,7 +467,10 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in /// public void Dispose() { - + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; } } } diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 2fe0945..7233db8 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -152,5 +152,12 @@ public void RemoveTask(ITask task) public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) { } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } } } diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriResource.cs index 4daee92..56acc02 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriResource.cs @@ -32,7 +32,7 @@ public UriResource ExtendedPath { get { - return new UriResource(Skip(ResourceRoot.PathSegments.Count()).PathSegments?.ToArray()); + return new UriResource(Skip(EndpointRoot.PathSegments.Count()).PathSegments?.ToArray()); } } @@ -76,14 +76,9 @@ public virtual string Display public bool Empty => !PathSegments.Any(); /// - /// Returns the root of the resource. + /// Returns the root of the endpoint. /// - public virtual UriResource ResourceRoot { get; set; } - - /// - /// Returns the root of the module. - /// - public virtual UriResource ModuleRoot { get; set; } + public virtual UriResource EndpointRoot { get; set; } /// /// Returns the root of the application. @@ -214,8 +209,7 @@ public UriResource(UriResource uri) Fragment = uri?.Fragment; ServerRoot = uri?.ServerRoot; ApplicationRoot = uri?.ApplicationRoot; - ModuleRoot = uri?.ModuleRoot; - ResourceRoot = uri?.ResourceRoot; + EndpointRoot = uri?.EndpointRoot; } /// @@ -242,8 +236,7 @@ public UriResource(UriResource uri, IEnumerable segments) { ServerRoot = uri.ServerRoot; ApplicationRoot = uri.ApplicationRoot; - ModuleRoot = uri.ModuleRoot; - ResourceRoot = uri.ResourceRoot; + EndpointRoot = uri.EndpointRoot; } /// @@ -257,8 +250,7 @@ public UriResource(UriResource uri, IEnumerable segments, IEnum { ServerRoot = uri.ServerRoot; ApplicationRoot = uri.ApplicationRoot; - ModuleRoot = uri.ModuleRoot; - ResourceRoot = uri.ResourceRoot; + EndpointRoot = uri.EndpointRoot; } /// From 97f3f3b87878a993fe469525452b770d42ff32c9 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 25 Oct 2024 21:07:11 +0200 Subject: [PATCH 032/162] add eventhandlers in endpointmanager --- .../WebEndpoint/EndpointManager.cs | 67 +++++++-------- .../WebEndpoint/EndpointRegistration.cs | 12 ++- src/WebExpress.WebCore/WebPage/PageManager.cs | 62 +++++++------- .../WebPlugin/PluginManager.cs | 10 --- .../WebResource/ResourceManager.cs | 26 +++--- .../WebRestAPI/RestApiManager.cs | 84 ++++++++++--------- 6 files changed, 128 insertions(+), 133 deletions(-) diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index aeae425..1a49807 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -5,7 +5,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebEndpoint { @@ -19,14 +18,14 @@ public sealed class EndpointManager : IEndpointManager, ISystemComponent private readonly Dictionary _registrations = []; /// - /// An event that fires when an resource is added. + /// An event that fires when an endpoint is added. /// - public event EventHandler AddResource; + public event EventHandler AddEndpoint; /// - /// An event that fires when an resource is removed. + /// An event that fires when an endpoint is removed. /// - public event EventHandler RemoveResource; + public event EventHandler RemoveEndpoint; /// /// Returns all endpoints contexts. @@ -54,13 +53,16 @@ private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServe /// Registers an endpoint context type. /// /// The type of the endpoint context. - /// The registration details containing the callback functions. - public void Register(EndpointRegistration registration) where T : IEndpointContext + /// The registration details containing the callback functions. + public void Register(EndpointRegistration endpointRegistration) where T : IEndpointContext { var type = typeof(T); if (!_registrations.ContainsKey(type)) { - _registrations[type] = registration; + _registrations[type] = endpointRegistration; + + endpointRegistration.AddEndpoint += OnAddEndpoint; + endpointRegistration.RemoveEndpoint += OnRemoveEndpoint; } } @@ -71,7 +73,10 @@ public void Register(EndpointRegistration registration) where T : IEndpointCo public void Remove() where T : IEndpointContext { var type = typeof(T); - _registrations.Remove(type); + _registrations.Remove(type, out var endpointRegistration); + + endpointRegistration.AddEndpoint -= OnAddEndpoint; + endpointRegistration.RemoveEndpoint -= OnRemoveEndpoint; } //// @@ -85,30 +90,6 @@ public IEnumerable GetEndpoints(Type endpointType, IApplicatio return _registrations.SelectMany(x => x.Value.EndpointResolver(endpointType, applicationContext)); } - ///// - ///// Creates a new instance or if caching is active, a possibly existing instance is returned. - ///// - ///// The endpoint context. - ///// The uri. - ///// The search context. - ///// The created endpoint. - //private IEndpoint CreateEndpoint(IEndpointContext endpointContext, UriResource uri, SearchContext searchContext) - //{ - // if (endpointContext == null) - // { - // return null; - // } - - // var type = endpointContext.GetType(); - - // if (_registrations.TryGetValue(type, out var registration)) - // { - // return registration.Factory(endpointContext, uri, searchContext.Culture); - // } - - // throw new InvalidOperationException($"No factory registered for type {type}"); - //} - /// /// Handles a request and returns a response. /// @@ -126,13 +107,23 @@ public Response HandleRequest(Request request, IEndpointContext endpointContext) } /// - /// Information about the component is collected and prepared for output in the log. + /// Handles the event when an endpoint is added. + /// + /// The source of the event. + /// The context of the endpoint being added. + private void OnAddEndpoint(object sender, IEndpointContext endpointContext) + { + AddEndpoint?.Invoke(sender, endpointContext); + } + + /// + /// Handles the event when an endpoint is removed. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + /// The source of the event. + /// The context of the endpoint being removed. + private void OnRemoveEndpoint(object sender, IEndpointContext endpointContext) { + RemoveEndpoint?.Invoke(sender, endpointContext); } /// diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs index 21e4ba6..72b82b6 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointRegistration.cs @@ -10,6 +10,16 @@ namespace WebExpress.WebCore.WebEndpoint /// public class EndpointRegistration { + /// + /// Stores the event handler for adding an endpoint. + /// + public EventHandler AddEndpoint { get; set; } + + /// + /// Stores the event handler for removing an endpoint. + /// + public EventHandler RemoveEndpoint { get; set; } + /// /// Returns or sets the context resolver function to resolve the corresponding endpoint contexts. /// @@ -24,7 +34,5 @@ public class EndpointRegistration /// Returns or sets the function to handle requests. /// public Func HandleRequest { get; set; } - - } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 9326de7..5f82c9e 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -57,40 +57,42 @@ private PageManager(IComponentHub componentManager, IHttpServerContext httpServe _componentHub.ApplicationManager.AddApplication += OnAddApplication; _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentHub.EndpointManager.Register - ( - new EndpointRegistration() + var endpointtRegistration = new EndpointRegistration() + { + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetPages(type, applicationContext) : GetPages(type), + EndpointsResolver = () => Pages, + HandleRequest = (request, endpontContext) => { - EndpointResolver = (type, applicationContext) => applicationContext != null ? GetPages(type, applicationContext) : GetPages(type), - EndpointsResolver = () => Pages, - HandleRequest = (request, endpontContext) => + var page = CreatePageInstance(endpontContext as IPageContext, request.Culture); + var pageType = page.GetType(); + var context = default(IRenderContext); + var pageContetx = endpontContext as IPageContext; + + if (pageType.IsGenericType) { - var page = CreatePageInstance(endpontContext as IPageContext, request.Culture); - var pageType = page.GetType(); - var context = default(IRenderContext); - var pageContetx = endpontContext as IPageContext; - - if (pageType.IsGenericType) - { - var typeOfT = pageType.GetGenericArguments()[0]; - var parameters = new object[] { page, endpontContext as IPageContext, request }; - - context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; - } - else - { - context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); - } - - page.Process(context); - - return new ResponseOK() - { - Content = context.VisualTree.Render(new VisualTreeContext(context)) - }; + var typeOfT = pageType.GetGenericArguments()[0]; + var parameters = new object[] { page, endpontContext as IPageContext, request }; + + context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; } + else + { + context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); + } + + page.Process(context); + + return new ResponseOK() + { + Content = context.VisualTree.Render(new VisualTreeContext(context)) + }; } - ); + }; + + AddPage += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemovePage += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + + _componentHub.EndpointManager.Register(endpointtRegistration); _httpServerContext = httpServerContext; diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index d348972..c8097f7 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -48,16 +48,6 @@ private PluginManager(IComponentHub componentManager, IHttpServerContext httpSer { _componentManager = componentManager; - _componentManager.AddComponent += (s, e) => - { - //AssignToComponent(e); - }; - - _componentManager.RemoveComponent += (s, e) => - { - //DetachFromcomponent(e); - }; - _httpServerContext = httpServerContext; _httpServerContext.Log.Debug diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 2e154e3..07295cc 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -56,21 +56,23 @@ private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServe _componentHub.ApplicationManager.AddApplication += OnAddApplication; _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentHub.EndpointManager.Register - ( - new EndpointRegistration() + var endpointtRegistration = new EndpointRegistration() + { + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetResorces(type, applicationContext) : GetResorces(type), + EndpointsResolver = () => Resources, + HandleRequest = (request, endpointContext) => { - EndpointResolver = (type, applicationContext) => applicationContext != null ? GetResorces(type, applicationContext) : GetResorces(type), - EndpointsResolver = () => Resources, - HandleRequest = (request, endpointContext) => - { - var resourceContext = endpointContext as IResourceContext; - var resource = CreateResourceInstance(resourceContext, request.Culture) as IResource; + var resourceContext = endpointContext as IResourceContext; + var resource = CreateResourceInstance(resourceContext, request.Culture); - return resource.Process(request); - } + return resource.Process(request); } - ); + }; + + AddResource += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemoveResource += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + + _componentHub.EndpointManager.Register(endpointtRegistration); _httpServerContext = httpServerContext; diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index b41f018..342da79 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -59,57 +59,59 @@ private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServer _componentHub.ApplicationManager.AddApplication += OnAddApplication; _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; - _componentHub.EndpointManager.Register - ( - new EndpointRegistration() + var endpointtRegistration = new EndpointRegistration() + { + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetRestApi(type, applicationContext) : GetRestApi(type), + EndpointsResolver = () => RestApis, + HandleRequest = (request, endpointContext) => { - EndpointResolver = (type, applicationContext) => applicationContext != null ? GetRestApi(type, applicationContext) : GetRestApi(type), - EndpointsResolver = () => RestApis, - HandleRequest = (request, endpointContext) => - { - var restApiContext = endpointContext as IRestApiContext; - var restApi = CreatePageInstance(restApiContext, request.Culture) as IRestApi; + var restApiContext = endpointContext as IRestApiContext; + var restApi = CreatePageInstance(restApiContext, request.Culture) as IRestApi; - if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) + if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) + { + switch (request.Method) { - switch (request.Method) - { - case RequestMethod.POST: - restApi.CreateData(request); - - return new ResponseOK(); - case RequestMethod.GET: - var data = restApi.GetData(request); - if (data != null) + case RequestMethod.POST: + restApi.CreateData(request); + + return new ResponseOK(); + case RequestMethod.GET: + var data = restApi.GetData(request); + if (data != null) + { + var jsonData = JsonSerializer.Serialize(data, _jsonOptions); + var content = Encoding.UTF8.GetBytes(jsonData); + + return new ResponseOK { - var jsonData = JsonSerializer.Serialize(data, _jsonOptions); - var content = Encoding.UTF8.GetBytes(jsonData); + Content = content + }; + } - return new ResponseOK - { - Content = content - }; - } + return new ResponseOK(); + case RequestMethod.PATCH: + restApi.UpdateData(request); - return new ResponseOK(); - case RequestMethod.PATCH: - restApi.UpdateData(request); + return new ResponseOK(); + case RequestMethod.DELETE: + restApi.DeleteData(request); - return new ResponseOK(); - case RequestMethod.DELETE: - restApi.DeleteData(request); - - return new ResponseOK(); - } + return new ResponseOK(); } - - return new ResponseBadRequest() - { - Content = I18N.Translate("webexpress:restapimanager.methodnotsupported", request.Method.ToString()) - }; } + + return new ResponseBadRequest() + { + Content = I18N.Translate("webexpress:restapimanager.methodnotsupported", request.Method.ToString()) + }; } - ); + }; + + AddRestApi += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemoveRestApi += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + + _componentHub.EndpointManager.Register(endpointtRegistration); _httpServerContext = httpServerContext; From a37f500623cc4b7081044664d6655031381efe41 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 25 Oct 2024 22:27:38 +0200 Subject: [PATCH 033/162] taskmanager refactored and added tests - refactored: taskmanager for improved performance and maintainability - added: unit tests to ensure reliability and correctness - fixed: various bugs --- .../Manager/UnitTestJobManager.cs | 4 +- .../Manager/UnitTestTaskManager.cs | 116 ++++++++++++++++++ src/WebExpress.WebCore.Test/TestRestApiC.cs | 2 +- src/WebExpress.WebCore.Test/TestTask.cs | 36 ++++++ .../WebComponent/ComponentActivator.cs | 52 +++++++- .../WebComponent/ComponentHub.cs | 17 +-- .../WebComponent/IComponentHub.cs | 12 +- src/WebExpress.WebCore/WebTask/ITask.cs | 11 +- .../WebTask/ITaskManager.cs | 63 ++++++++++ .../WebTask/Model/TaskDictionary.cs | 2 +- src/WebExpress.WebCore/WebTask/Task.cs | 22 +++- src/WebExpress.WebCore/WebTask/TaskManager.cs | 74 ++++------- 12 files changed, 328 insertions(+), 83 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestTask.cs create mode 100644 src/WebExpress.WebCore/WebTask/ITaskManager.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs index 36927fd..950765b 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs @@ -63,9 +63,9 @@ public void IsIContext() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - foreach (var application in componentHub.ResourceManager.Resources) + foreach (var job in componentHub.JobManager.Jobs) { - Assert.True(typeof(IContext).IsAssignableFrom(application.GetType()), $"Resource context {application.GetType().Name} does not implement IContext."); + Assert.True(typeof(IContext).IsAssignableFrom(job.GetType()), $"Job context {job.GetType().Name} does not implement IContext."); } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs new file mode 100644 index 0000000..1533beb --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs @@ -0,0 +1,116 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the task manager. + /// + [Collection("NonParallelTests")] + public class UnitTestTaskManager + { + /// + /// Tests whether the task manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); + } + + /// + /// Tests whether the task context implements interface IComponent. + /// + [Fact] + public void IsCompopnent() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + componentHub.TaskManager.CreateTask("test"); + + // test execution + foreach (var task in componentHub.TaskManager.Tasks) + { + Assert.True(typeof(IComponent).IsAssignableFrom(task.GetType()), $"Task {task.GetType().Name} does not implement IComponent."); + } + } + + /// + /// Test the create task of the task manager. + /// + [Fact] + public void CreateSystemTask() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + var task = componentHub.TaskManager.CreateTask("test"); + Assert.Equal("test", task?.Id); + } + + /// + /// Test the create task of the task manager. + /// + [Fact] + public void CreateOwnTask() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + var task = componentHub.TaskManager.CreateTask("test", null, []); + Assert.Equal("test", task?.Id); + } + + /// + /// Test the contains task function of the task manager. + /// + [Fact] + public void ContainsTask() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var task = componentHub.TaskManager.CreateTask("test"); + + // test execution + var res = componentHub.TaskManager.ContainsTask("test"); + Assert.True(res); + } + + /// + /// Test the get task function of the task manager. + /// + [Fact] + public void GetTask() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var task = componentHub.TaskManager.CreateTask("test"); + + // test execution + var res = componentHub.TaskManager.GetTask("test"); + Assert.Equal(task, res); + } + + /// + /// Test the remove task function of the task manager. + /// + [Fact] + public void RemoveTask() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var task = componentHub.TaskManager.CreateTask("test"); + + // test execution + componentHub.TaskManager.RemoveTask(task); + Assert.Empty(componentHub.TaskManager.Tasks); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestRestApiC.cs b/src/WebExpress.WebCore.Test/TestRestApiC.cs index 553e721..70817e0 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiC.cs +++ b/src/WebExpress.WebCore.Test/TestRestApiC.cs @@ -83,7 +83,7 @@ public override void DeleteData(WebMessage.Request request) /// /// Release of unmanaged resources reserved during use. /// - public void Dispose() + public override void Dispose() { } } diff --git a/src/WebExpress.WebCore.Test/TestTask.cs b/src/WebExpress.WebCore.Test/TestTask.cs new file mode 100644 index 0000000..bfb27a3 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestTask.cs @@ -0,0 +1,36 @@ +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test task that implements the Task. + /// + public class TestTask : WebTask.Task + { + /// + /// Initializes a new instance of the class. + /// + /// The component hub used for dependency injection. + /// The unique identifier for the task. + /// The arguments for the task. + public TestTask(IComponentHub componentHub, string id, params object[] args) + : base(id, args) + { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + + if (id == null) + { + throw new ArgumentNullException(nameof(id), "Parameter cannot be null or empty."); + } + + if (args == null) + { + throw new ArgumentNullException(nameof(args), "Parameter cannot be null or empty."); + } + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index 4a9c5f4..e291f50 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -27,7 +27,7 @@ public static T CreateInstance(Type responseType, IComponentHub componentHub, if (constructors != null) { - foreach (var constructor in constructors) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) { // injection var parameters = constructor.GetParameters(); @@ -57,7 +57,7 @@ public static T CreateInstance(Type responseType, IComponentHub componentHub, /// /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// - /// The type of the component, which must implement . + /// The type of the component manager, which must implement . /// The type of the component to create. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. @@ -69,7 +69,7 @@ public static T CreateInstance(Type componentType, IComponentHub componentHub if (constructors != null) { - foreach (var constructor in constructors) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) { // injection var parameters = constructor.GetParameters(); @@ -95,6 +95,48 @@ public static T CreateInstance(Type componentType, IComponentHub componentHub return Activator.CreateInstance(componentType) as T; } + /// + /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. + /// + /// The type of the component, which must implement . + /// The component hub to use for dependency injection. + /// Additional parameters to pass to the component's constructor. + /// An instance of the specified component type. + public static T CreateInstance(IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var componentType = typeof(T); + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? + advancedParameters.Where(x => x != null) + .Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return Activator.CreateInstance(componentType) as T; + } + /// /// Creates an instance of the specified component type with the provided context and component hub and advanced parameters. /// @@ -112,7 +154,7 @@ public static T CreateInstance(Type componentType, C context, IComponentHu if (constructors != null) { - foreach (var constructor in constructors) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) { // injection var parameters = constructor.GetParameters(); @@ -156,7 +198,7 @@ public static IComponent CreateInstance(Type componentType, C context, ICompo if (constructors != null) { - foreach (var constructor in constructors) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) { // injection var parameters = constructor.GetParameters(); diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 96807f9..067e2cb 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -38,6 +38,7 @@ public class ComponentHub : IComponentHub private readonly SessionManager _sessionManager; private readonly EventManager _eventManager; private readonly JobManager _jobManager; + private readonly TaskManager _taskManager; /// /// An event that fires when an component is added. @@ -73,7 +74,7 @@ public class ComponentHub : IComponentHub _statusPageManager, _internationalizationManager, _sessionManager, - TaskManager + _taskManager }.Concat(_dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); /// @@ -112,6 +113,12 @@ public class ComponentHub : IComponentHub /// The instance of the job manager. public IJobManager JobManager => _jobManager; + /// + /// Returns the task manager. + /// + /// The instance of the task manager. + public ITaskManager TaskManager => _taskManager; + /// /// Returns the endpoint manager. /// @@ -160,12 +167,6 @@ public class ComponentHub : IComponentHub /// The instance of the session manager. public ISessionManager SessionManager => _sessionManager; - /// - /// Returns the task manager. - /// - /// The instance of the task manager manager. - public TaskManager TaskManager { get; private set; } - /// /// Initializes a new instance of the class. /// @@ -189,7 +190,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _eventManager = CreateInstance(typeof(EventManager)) as EventManager; _jobManager = CreateInstance(typeof(JobManager)) as JobManager; _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; - TaskManager = CreateInstance(typeof(TaskManager)) as TaskManager; + _taskManager = CreateInstance(typeof(TaskManager)) as TaskManager; _internationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 8b2ca2d..6f5dfa8 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -78,6 +78,12 @@ public interface IComponentHub : IComponentManager /// The instance of the job manager. IJobManager JobManager { get; } + /// + /// Returns the task manager. + /// + /// The instance of the task manager. + ITaskManager TaskManager { get; } + /// /// Returns the endpoint manager. /// @@ -126,12 +132,6 @@ public interface IComponentHub : IComponentManager /// The instance of the session manager. ISessionManager SessionManager { get; } - /// - /// Returns the task manager. - /// - /// The instance of the task manager manager. - TaskManager TaskManager { get; } - /// /// Returns a component based on its id. /// diff --git a/src/WebExpress.WebCore/WebTask/ITask.cs b/src/WebExpress.WebCore/WebTask/ITask.cs index cfa99bb..0253c80 100644 --- a/src/WebExpress.WebCore/WebTask/ITask.cs +++ b/src/WebExpress.WebCore/WebTask/ITask.cs @@ -1,8 +1,12 @@ using System; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebTask { - public interface ITask + /// + /// Represents a task that can be executed, monitored, and controlled. + /// + public interface ITask : IComponent { /// /// Event is triggered when the task is executed. @@ -34,11 +38,6 @@ public interface ITask /// string Message { get; set; } - /// - /// Initialization - /// - void Initialization(); - /// /// Starts the execution concurrently. /// diff --git a/src/WebExpress.WebCore/WebTask/ITaskManager.cs b/src/WebExpress.WebCore/WebTask/ITaskManager.cs new file mode 100644 index 0000000..105ce16 --- /dev/null +++ b/src/WebExpress.WebCore/WebTask/ITaskManager.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebTask +{ + /// + /// Management of ad-hoc tasks. + /// + public interface ITaskManager : IComponentManager + { + /// + /// Returns the collection of tasks. + /// + IEnumerable Tasks { get; } + + /// + /// Checks if a task has already been created. + /// + /// The id of the task. + /// True if this task already exists, false otherwise. + bool ContainsTask(string id); + + /// + /// Returns an existing task. + /// + /// The id of the task. + /// The task or null. + ITask GetTask(string id); + + /// + /// Creates a new task or returns an existing task. + /// + /// The id of the task. + /// The event argument. + /// The task or null. + ITask CreateTask(string id, params object[] args); + + /// + /// Creates a new task or returns an existing task. + /// + /// The id of the task. + /// The event handler. + /// The event argument. + /// The task or null. + ITask CreateTask(string id, EventHandler handler, params object[] args); + + /// + /// Creates a new task or returns an existing task. + /// + /// The id of the task. + /// The event handler. + /// The event argument. + /// The task or null. + ITask CreateTask(string id, EventHandler handler, params object[] args) where T : Task; + + /// + /// Removes a task. + /// + /// The task. + void RemoveTask(ITask task); + } +} diff --git a/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs b/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs index 7a78172..f6ed37b 100644 --- a/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs +++ b/src/WebExpress.WebCore/WebTask/Model/TaskDictionary.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebTask.Model /// Key = The task id. /// Value = The task. /// - internal class TaskDictionary : Dictionary + internal class TaskDictionary : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebTask/Task.cs b/src/WebExpress.WebCore/WebTask/Task.cs index 96b5aac..00c713d 100644 --- a/src/WebExpress.WebCore/WebTask/Task.cs +++ b/src/WebExpress.WebCore/WebTask/Task.cs @@ -25,17 +25,17 @@ public class Task : ITask /// /// Returns the id of the task. /// - public string Id { get; internal set; } + public string Id { get; private set; } /// /// Returns the state in which the task is located. /// - public TaskState State { get; internal set; } + public TaskState State { get; protected set; } = TaskState.Created; /// /// The arguments. /// - public ICollection Arguments { get; internal set; } + public ICollection Arguments { get; private set; } /// /// Thread termination of the task. @@ -57,10 +57,14 @@ public int Progress public string Message { get; set; } /// - /// Initialization + /// Initializes a new instance of the class. /// - public virtual void Initialization() + /// The unique identifier for the task. + /// The arguments for the task. + public Task(string id, params object[] args) { + Id = id; + Arguments = args; } /// @@ -114,5 +118,13 @@ public void Cancel() WebEx.ComponentHub.TaskManager.RemoveTask(this); } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + Cancel(); + } } } diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 7233db8..028b125 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebTask.Model; namespace WebExpress.WebCore.WebTask @@ -10,34 +9,28 @@ namespace WebExpress.WebCore.WebTask /// /// Management of ad-hoc tasks. /// - public class TaskManager : IComponentManager, ISystemComponent + public class TaskManager : ITaskManager, ISystemComponent { - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly TaskDictionary _dictionary = []; /// - /// Returns the directory in which the active jobs are listed. + /// Returns the collection of tasks. /// - private TaskDictionary Dictionary { get; } = new TaskDictionary(); + public IEnumerable Tasks => _dictionary.Values; /// /// Initializes a new instance of the class. /// - internal TaskManager() - { - } - - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) + /// The component hub. + /// The reference to the context of the host. + private TaskManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - HttpServerContext = context; + _componentHub = componentHub; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:applicationmanager.initialization") ); @@ -50,7 +43,7 @@ public void Initialization(IHttpServerContext context) /// True if this task already exists, false otherwise. public bool ContainsTask(string id) { - return Dictionary.ContainsKey(id?.ToLower()); + return _dictionary.ContainsKey(id?.ToLower()); } /// @@ -60,9 +53,9 @@ public bool ContainsTask(string id) /// The task or null. public ITask GetTask(string id) { - if (Dictionary.ContainsKey(id?.ToLower())) + if (_dictionary.ContainsKey(id?.ToLower())) { - return Dictionary[id?.ToLower()]; + return _dictionary[id?.ToLower()]; } return null; @@ -78,17 +71,15 @@ public ITask CreateTask(string id, params object[] args) { var key = id?.ToLower(); - if (!Dictionary.ContainsKey(id)) + if (!_dictionary.ContainsKey(id)) { - var task = new Task() { Id = id, State = TaskState.Created, Arguments = args }; - Dictionary.Add(key, task); - - task.Initialization(); + var task = ComponentActivator.CreateInstance(_componentHub, [id, args]); + _dictionary.Add(key, task); return task; } - return Dictionary[id]; + return _dictionary[id]; } /// @@ -110,23 +101,21 @@ public ITask CreateTask(string id, EventHandler handler, params o /// The event handler. /// The event argument. /// The task or null. - public ITask CreateTask(string id, EventHandler handler, params object[] args) where T : Task, new() + public ITask CreateTask(string id, EventHandler handler, params object[] args) where T : Task { var key = id?.ToLower(); - if (!Dictionary.ContainsKey(id)) + if (!_dictionary.ContainsKey(id)) { - var task = new Task() { Id = id, State = TaskState.Created, Arguments = args }; - Dictionary.Add(key, task); - - task.Initialization(); + var task = ComponentActivator.CreateInstance(_componentHub, [id, args]); + _dictionary.Add(key, task); task.Process += handler; return task; } - return Dictionary[id]; + return _dictionary[id]; } /// @@ -137,20 +126,7 @@ public void RemoveTask(ITask task) { var key = task?.Id.ToLower(); - if (Dictionary.ContainsKey(key)) - { - Dictionary.Remove(key); - } - } - - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { + _dictionary.Remove(key); } /// From d0774f8c42ef803f989424594a00f260c18cec49 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 25 Oct 2024 22:31:15 +0200 Subject: [PATCH 034/162] updated: documentation --- src/WebExpress.WebCore/WebTask/Task.cs | 1 - src/WebExpress.WebCore/WebTask/TaskEventArgs.cs | 3 +++ src/WebExpress.WebCore/WebTask/TaskState.cs | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore/WebTask/Task.cs b/src/WebExpress.WebCore/WebTask/Task.cs index 00c713d..c65416f 100644 --- a/src/WebExpress.WebCore/WebTask/Task.cs +++ b/src/WebExpress.WebCore/WebTask/Task.cs @@ -4,7 +4,6 @@ namespace WebExpress.WebCore.WebTask { - /// /// Represents a task that can be executed asynchronously. /// diff --git a/src/WebExpress.WebCore/WebTask/TaskEventArgs.cs b/src/WebExpress.WebCore/WebTask/TaskEventArgs.cs index 7a1c677..6d9fd3a 100644 --- a/src/WebExpress.WebCore/WebTask/TaskEventArgs.cs +++ b/src/WebExpress.WebCore/WebTask/TaskEventArgs.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebTask { + /// + /// Provides data for a task event. + /// public class TaskEventArgs : EventArgs { } diff --git a/src/WebExpress.WebCore/WebTask/TaskState.cs b/src/WebExpress.WebCore/WebTask/TaskState.cs index 33c1b5e..a5e8d6b 100644 --- a/src/WebExpress.WebCore/WebTask/TaskState.cs +++ b/src/WebExpress.WebCore/WebTask/TaskState.cs @@ -1,5 +1,9 @@ namespace WebExpress.WebCore.WebTask { + + /// + /// Represents the various states a task can be in. + /// public enum TaskState { /// From 84d9f6a08ece3833660d98ff2813eea1414551a2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 25 Oct 2024 22:31:46 +0200 Subject: [PATCH 035/162] bug fixes --- src/WebExpress.WebCore/WebResource/ResourceAsset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index 3b5d7da..afe1a04 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -152,7 +152,7 @@ private static byte[] GetData(string file, Assembly assembly, IEnumerable /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. /// - public void Dispose() + public override void Dispose() { } From 0003ab01c6ead5a88adb4ef464e89aba2dd5d890 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 26 Oct 2024 09:29:33 +0200 Subject: [PATCH 036/162] changes to log and packagemanager with added tests - refactored: log and packagemanager for improved functionality - added: corresponding unit tests to ensure reliability and correctness - optimized: overall project code for better performance and maintainability --- .../Fixture/UnitTestControlFixture.cs | 23 ++-- .../Manager/UnitTestLog.cs | 55 +++++++++ .../Manager/UnitTestPackage.cs | 55 +++++++++ .../Manager/UnitTestPlugin.cs | 2 +- .../Manager/UnitTestSitemapManager.cs | 5 +- .../InternationalizationManager.cs | 30 ++--- .../WebApplication/ApplicationManager.cs | 110 ++++++++++-------- .../WebComponent/ComponentActivator.cs | 35 +++--- .../WebComponent/ComponentHub.cs | 69 ++++------- .../WebComponent/IComponentHub.cs | 10 +- .../WebEndpoint/EndpointManager.cs | 2 +- .../WebEvent/EventManager.cs | 2 +- .../WebEvent/Model/EventItem.cs | 5 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 32 +++-- .../WebJob/Model/ScheduleItem.cs | 9 +- src/WebExpress.WebCore/WebLog/ILogManager.cs | 27 +++++ src/WebExpress.WebCore/WebLog/LogManager.cs | 91 +++++++-------- .../WebPackage/IPackageManager.cs | 27 +++++ .../WebPackage/PackageManager.cs | 82 ++++++------- src/WebExpress.WebCore/WebPage/PageManager.cs | 48 ++++---- .../WebPlugin/PluginManager.cs | 36 +++--- .../WebResource/ResourceManager.cs | 22 ++-- .../WebRestAPI/RestApiManager.cs | 22 ++-- .../WebSitemap/SitemapManager.cs | 42 +++---- .../WebStatusPage/StatusPageManager.cs | 30 +++-- src/WebExpress.WebCore/WebTask/TaskManager.cs | 27 ++--- 26 files changed, 516 insertions(+), 382 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs create mode 100644 src/WebExpress.WebCore/WebLog/ILogManager.cs create mode 100644 src/WebExpress.WebCore/WebPackage/IPackageManager.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 66966ea..32cc6cc 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -14,12 +14,18 @@ namespace WebExpress.WebCore.Test.Fixture { + /// + /// A fixture class for unit tests, providing various mock objects and utility methods. + /// public class UnitTestControlFixture : IDisposable { + private static readonly string[] _separator = ["\r\n", "\r", "\n"]; + /// - /// Returns the + /// Returns the list of resources. /// - private static List Ressources { get; } = new List(); + private static List Ressources { get; } = []; + /// /// Initializes a new instance of the class and boot the component manager. @@ -52,7 +58,7 @@ public static IHttpServerContext CreateHttpServerContextMock() /// /// Create a component hub. /// - /// The component manager. + /// The component hub. public static ComponentHub CreateComponentHubMock() { var ctorComponentManager = typeof(ComponentHub).GetConstructor @@ -77,7 +83,7 @@ public static ComponentHub CreateComponentHubMock() /// /// Create a component hub and register the plugins. /// - /// The component manager. + /// The component hub. public static ComponentHub CreateAndRegisterComponentHubMock() { var componentManager = CreateComponentHubMock(); @@ -110,10 +116,10 @@ public static WebMessage.HttpContext CreateHttpContextMock(string content = "") var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); var featureCollection = new FeatureCollection(); var firstLine = content.Split('\n').FirstOrDefault(); - var lines = content.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); + var lines = content.Split(_separator, StringSplitOptions.None); var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); var pos = content.Length > 0 ? content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4 : 0; - var innerContent = pos < content.Length ? content.Substring(pos) : ""; + var innerContent = pos < content.Length ? content[pos..] : ""; var contentBytes = Encoding.UTF8.GetBytes(innerContent); var requestFeature = new HttpRequestFeature @@ -166,7 +172,7 @@ public static WebMessage.HttpContext CreateHttpContextMock(string content = "") featureCollection.Set(connectionFeature); var componentManager = CreateComponentHubMock(); - var context = new WebMessage.HttpContext(featureCollection, componentManager.HttpServerContext); + var context = new WebMessage.HttpContext(featureCollection, CreateHttpServerContextMock()); return context; } @@ -191,7 +197,7 @@ public static PageContext CreratePageContextMock() var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IApplicationContext)], null); var applicationContext = WebEx.ComponentHub.ApplicationManager.Applications - .Where(x => x.ApplicationId == typeof(TestApplicationA).FullName.ToLower()) + .Where(x => x.ApplicationId.Equals(typeof(TestApplicationA).FullName, StringComparison.CurrentCultureIgnoreCase)) .FirstOrDefault(); var pageContext = (PageContext)ctorPageContext.Invoke([applicationContext]); @@ -221,6 +227,7 @@ public static string GetEmbeddedResource(string fileName) /// public void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs new file mode 100644 index 0000000..e695909 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs @@ -0,0 +1,55 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the log manager. + /// + [Collection("NonParallelTests")] + public class UnitTestLog + { + /// + /// Test the register function of the log manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var logManager = componentHub.LogManager as LogManager; + + // test execution + Assert.NotNull(logManager); + } + + /// + /// Test the remove function of the log manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var logManager = componentHub.LogManager as LogManager; + + // test execution + Assert.NotNull(logManager); + } + + /// + /// Tests whether the log manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var logManager = componentHub.LogManager as LogManager; + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(logManager.GetType())); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs new file mode 100644 index 0000000..bb8cc67 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs @@ -0,0 +1,55 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPackage; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the package manager. + /// + [Collection("NonParallelTests")] + public class UnitTestPackage + { + /// + /// Test the register function of the package manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var packageManager = componentHub.PackageManager as PackageManager; + + // test execution + Assert.NotNull(packageManager); + } + + /// + /// Test the remove function of the package manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var packageManager = componentHub.PackageManager as PackageManager; + + // test execution + Assert.NotNull(packageManager); + } + + /// + /// Tests whether the package manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var packageManager = componentHub.PackageManager as PackageManager; + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(packageManager.GetType())); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs index 42f6140..f4a608f 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs @@ -235,7 +235,7 @@ public void IsIComponentManager() var pluginManager = componentHub.PluginManager as PluginManager; // test execution - Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.PluginManager.GetType())); + Assert.True(typeof(IComponentManager).IsAssignableFrom(pluginManager.GetType())); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index dced294..d35634b 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -60,13 +60,14 @@ public void SearchResource(string uri, string id) // preconditions var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); var context = UnitTestControlFixture.CreateHttpContextMock(); + var httpServerContext = UnitTestControlFixture.CreateHttpServerContextMock(); componentHub.SitemapManager.Refresh(); // test execution var searchResult = componentHub.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() { - HttpServerContext = componentHub.HttpServerContext, - Culture = componentHub.HttpServerContext.Culture, + HttpServerContext = httpServerContext, + Culture = httpServerContext.Culture, HttpContext = context }); diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index a4e8270..36a797e 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -15,7 +15,7 @@ namespace WebExpress.WebCore.Internationalization /// public sealed class InternationalizationManager : IInternationalizationManager, IComponentManagerPlugin, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; /// /// Returns the default language. @@ -25,7 +25,7 @@ public sealed class InternationalizationManager : IInternationalizationManager, /// /// Returns the directory by listing the internationalization key-value pairs. /// - private static InternationalizationDictionary Dictionary { get; } = new InternationalizationDictionary(); + private static InternationalizationDictionary Dictionary { get; } = []; /// /// Returns or sets the reference to the context of the host. @@ -35,18 +35,18 @@ public sealed class InternationalizationManager : IInternationalizationManager, /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private InternationalizationManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private InternationalizationManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentHub; - _componentManager.PluginManager.AddPlugin += (sender, pluginContext) => + _componentHub.PluginManager.AddPlugin += (sender, pluginContext) => { Register(pluginContext); }; - _componentManager.PluginManager.RemovePlugin += (sender, pluginContext) => + _componentHub.PluginManager.RemovePlugin += (sender, pluginContext) => { Remove(pluginContext); }; @@ -102,12 +102,13 @@ internal void Register(Assembly assembly, string pluginId) { var language = languageResource.Split('.').LastOrDefault()?.ToLower(); - if (!Dictionary.ContainsKey(language)) + if (!Dictionary.TryGetValue(language, out InternationalizationItem value)) { - Dictionary.Add(language, []); + value = ([]); + Dictionary.Add(language, value); } - var dictItem = Dictionary[language]; + var dictItem = value; using var stream = assembly.GetManifestResourceStream(languageResource); using var streamReader = new StreamReader(stream); @@ -126,6 +127,8 @@ internal void Register(Assembly assembly, string pluginId) } } } + + Log(); } /// @@ -148,6 +151,8 @@ public void Remove(IPluginContext pluginContext) dictionary.Remove(key); } } + + Log(); } /// @@ -294,10 +299,7 @@ public string Translate(CultureInfo culture, string pluginId, string key, params /// /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { } diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 6dbf2bf..18c421b 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebApplication.Model; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -49,7 +50,7 @@ private ApplicationManager(IComponentHub componentHub, IHttpServerContext httpSe _componentHub.PluginManager.AddPlugin += OnAddPlugin; _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; - _httpServerContext = _componentHub.HttpServerContext; + _httpServerContext = httpServerContext; _httpServerContext.Log.Debug ( @@ -138,6 +139,7 @@ private void Register(IPluginContext pluginContext) ( type, applicationContext, + _httpServerContext, _componentHub ); @@ -166,6 +168,8 @@ private void Register(IPluginContext pluginContext) ); } } + + Log(); } /// @@ -179,17 +183,17 @@ internal void Remove(IPluginContext pluginContext) return; } - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) { - return; - } + foreach (var applicationContext in value) + { + OnRemoveApplication(applicationContext.Value.ApplicationContext); + } - foreach (var applicationContext in _dictionary[pluginContext]) - { - OnRemoveApplication(applicationContext.Value.ApplicationContext); + _dictionary.Remove(pluginContext); } - _dictionary.Remove(pluginContext); + Log(); } /// @@ -263,12 +267,12 @@ public IEnumerable GetApplications(IEnumerable appl /// The contexts of the applications as an enumeration. public IEnumerable GetApplications(IPluginContext pluginContext) { - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) { - return new List(); + return value.Values.Select(x => x.ApplicationContext); } - return _dictionary[pluginContext].Values.Select(x => x.ApplicationContext); + return []; } /// @@ -298,7 +302,39 @@ public void Boot(IPluginContext pluginContext) return; } - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) + { + foreach (var applicationItem in value?.Values ?? Enumerable.Empty()) + { + var token = applicationItem.CancellationTokenSource.Token; + + // Run the application concurrently + Task.Run(() => + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:applicationmanager.application.processing.start", + applicationItem.ApplicationContext.ApplicationId) + ); + + applicationItem.Application.Run(); + + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:applicationmanager.application.processing.end", + applicationItem.ApplicationContext.ApplicationId + ) + ); + + token.ThrowIfCancellationRequested(); + }, token); + } + } + else { _httpServerContext.Log.Warning ( @@ -308,38 +344,6 @@ public void Boot(IPluginContext pluginContext) pluginContext.PluginId ) ); - - return; - } - - foreach (var applicationItem in _dictionary[pluginContext]?.Values ?? Enumerable.Empty()) - { - var token = applicationItem.CancellationTokenSource.Token; - - // Run the application concurrently - Task.Run(() => - { - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:applicationmanager.application.processing.start", - applicationItem.ApplicationContext.ApplicationId) - ); - - applicationItem.Application.Run(); - - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress:applicationmanager.application.processing.end", - applicationItem.ApplicationContext.ApplicationId - ) - ); - - token.ThrowIfCancellationRequested(); - }, token); } } @@ -396,19 +400,23 @@ private void OnRemovePlugin(object sender, IPluginContext e) /// /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { - foreach (var applicationContext in GetApplications(pluginContext)) + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List { - output.Add + I18N.Translate("webexpress:applicationmanager") + }; + + foreach (var applicationContext in Applications) + { + list.Add ( - string.Empty.PadRight(deep) + I18N.Translate("webexpress:applicationmanager.application", applicationContext.ApplicationId) ); } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index e291f50..ee6debf 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -16,18 +16,19 @@ public static class ComponentActivator /// /// The type of the response. /// The type of the response to create. + /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameter with a status message to pass to the response's constructor. /// Additional parameters to pass to the component's constructor. /// An instance of the specified response type. - public static T CreateInstance(Type responseType, IComponentHub componentHub, StatusMessage statusMessage, params object[] advancedParameters) where T : Response + public static T CreateInstance(Type responseType, IHttpServerContext httpServerContext, IComponentHub componentHub, StatusMessage statusMessage, params object[] advancedParameters) where T : Response { var flags = BindingFlags.NonPublic | BindingFlags.Instance; var constructors = responseType?.GetConstructors(flags); if (constructors != null) { - foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) { // injection var parameters = constructor.GetParameters(); @@ -36,7 +37,7 @@ public static T CreateInstance(Type responseType, IComponentHub componentHub, var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(StatusMessage) ? statusMessage : parameter.ParameterType == typeof(IComponentHub) ? componentHub : - parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? .GetValue(componentHub) ?? @@ -58,18 +59,19 @@ public static T CreateInstance(Type responseType, IComponentHub componentHub, /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// /// The type of the component manager, which must implement . + /// The reference to the context of the host. /// The type of the component to create. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponentManager + public static T CreateInstance(Type componentType, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponentManager { var flags = BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); if (constructors != null) { - foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) { // injection var parameters = constructor.GetParameters(); @@ -77,7 +79,7 @@ public static T CreateInstance(Type componentType, IComponentHub componentHub var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : - parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? .GetValue(componentHub) ?? @@ -99,10 +101,11 @@ public static T CreateInstance(Type componentType, IComponentHub componentHub /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// /// The type of the component, which must implement . + /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent + public static T CreateInstance(IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var componentType = typeof(T); @@ -110,7 +113,7 @@ public static T CreateInstance(IComponentHub componentHub, params object[] ad if (constructors != null) { - foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) { // injection var parameters = constructor.GetParameters(); @@ -118,7 +121,7 @@ public static T CreateInstance(IComponentHub componentHub, params object[] ad var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : - parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? .GetValue(componentHub) ?? @@ -144,17 +147,18 @@ public static T CreateInstance(IComponentHub componentHub, params object[] ad /// The type of the context, which must implement . /// The type of the component to create. /// The context to pass to the component's constructor. + /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, C context, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent where C : IContext + public static T CreateInstance(Type componentType, C context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent where C : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); if (constructors != null) { - foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) { // injection var parameters = constructor.GetParameters(); @@ -162,7 +166,7 @@ public static T CreateInstance(Type componentType, C context, IComponentHu var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : - parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : parameter.ParameterType == typeof(C) ? context : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? @@ -188,17 +192,18 @@ public static T CreateInstance(Type componentType, C context, IComponentHu /// The type of the context, which must implement . /// The type of the component to create. /// The context to pass to the component's constructor. + /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static IComponent CreateInstance(Type componentType, C context, IComponentHub componentHub, params object[] advancedParameters) where C : IContext + public static IComponent CreateInstance(Type componentType, C context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where C : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); if (constructors != null) { - foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Count())) + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) { // injection var parameters = constructor.GetParameters(); @@ -206,7 +211,7 @@ public static IComponent CreateInstance(Type componentType, C context, ICompo var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : - parameter.ParameterType == typeof(IHttpServerContext) ? componentHub.HttpServerContext : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : parameter.ParameterType == typeof(C) ? context : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 067e2cb..5e6b305 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -25,7 +25,10 @@ namespace WebExpress.WebCore.WebComponent /// public class ComponentHub : IComponentHub { + private readonly IHttpServerContext _httpServerContext; private readonly ComponentDictionary _dictionary = []; + private readonly LogManager _logManager; + private readonly PackageManager _packageManager; private readonly InternationalizationManager _internationalizationManager; private readonly PluginManager _pluginManager; private readonly ApplicationManager _applicationManager; @@ -50,18 +53,13 @@ public class ComponentHub : IComponentHub /// public event EventHandler RemoveComponent; - /// - /// Returns the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - /// /// Returns all registered managers. /// public IEnumerable Managers => new IComponentManager[] { - LogManager, - PackageManager, + _logManager, + _packageManager, _pluginManager, _applicationManager, _endpointManager, @@ -81,13 +79,13 @@ public class ComponentHub : IComponentHub /// Returns the log manager. /// /// The instance of the log manager. - public LogManager LogManager { get; private set; } + public ILogManager LogManager => _logManager; /// /// Returns the package manager. /// /// The instance of the package manager. - public PackageManager PackageManager { get; private set; } + public IPackageManager PackageManager => _packageManager; /// /// Returns the plugin manager. @@ -173,12 +171,13 @@ public class ComponentHub : IComponentHub /// The reference to the context of the host. internal ComponentHub(IHttpServerContext httpServerContext) { - HttpServerContext = httpServerContext; + _httpServerContext = httpServerContext; // order is relevant - LogManager = CreateInstance(typeof(LogManager)) as LogManager; - PackageManager = CreateInstance(typeof(PackageManager)) as PackageManager; _pluginManager = CreateInstance(typeof(PluginManager)) as PluginManager; + _packageManager = CreateInstance(typeof(PackageManager)) as PackageManager; + + _logManager = CreateInstance(typeof(LogManager)) as LogManager; _internationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; @@ -194,7 +193,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _internationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( _internationalizationManager.Translate("webexpress:componentmanager.initialization") ); @@ -223,7 +222,7 @@ private IComponentManager CreateInstance(Type componentType) } else if (!componentType.GetInterfaces().Where(x => x == typeof(IComponentManager)).Any()) { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( _internationalizationManager.Translate ( @@ -237,11 +236,11 @@ private IComponentManager CreateInstance(Type componentType) try { - return ComponentActivator.CreateInstance(componentType, this); + return ComponentActivator.CreateInstance(componentType, _httpServerContext, this); } catch (Exception ex) { - HttpServerContext.Log.Exception(ex); + _httpServerContext.Log.Exception(ex); } return null; @@ -308,7 +307,7 @@ internal void Register(IPluginContext pluginContext) ComponentInstance = componentInstance }]); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( _internationalizationManager.Translate("webexpress:componentmanager.register", id) ); @@ -318,7 +317,7 @@ internal void Register(IPluginContext pluginContext) } else { - HttpServerContext.Log.Warning + _httpServerContext.Log.Warning ( _internationalizationManager.Translate("webexpress:componentmanager.duplicate", id) ); @@ -372,12 +371,12 @@ internal void BootComponent(IEnumerable pluginContexts) /// internal void Execute() { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( _internationalizationManager.Translate("webexpress:componentmanager.execute") ); - PackageManager.Execute(); + _packageManager.Execute(); _jobManager.Execute(); } @@ -386,7 +385,7 @@ internal void Execute() /// internal void ShutDown() { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( _internationalizationManager.Translate("webexpress:componentmanager.shutdown") ); @@ -431,7 +430,7 @@ public void Remove(IPluginContext pluginContext) { OnRemoveComponent(componentItem.ComponentInstance); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( _internationalizationManager.Translate("webexpress:componentmanager.remove") ); @@ -462,9 +461,9 @@ private void OnRemoveComponent(IComponentManager component) /// /// Output of the components to the log. /// - internal void LogStatus() + private void Log() { - using var frame = new LogFrameSimple(HttpServerContext.Log); + using var frame = new LogFrameSimple(_httpServerContext.Log); var output = new List { _internationalizationManager.Translate("webexpress:componentmanager.component") @@ -477,28 +476,9 @@ internal void LogStatus() string.Empty.PadRight(2) + _internationalizationManager.Translate("webexpress:pluginmanager.plugin", pluginContext.PluginId) ); - - _applicationManager.PrepareForLog(pluginContext, output, 4); - _resourceManager.PrepareForLog(pluginContext, output, 4); - _statusPageManager.PrepareForLog(pluginContext, output, 4); - _jobManager.PrepareForLog(pluginContext, output, 4); } - //foreach (var item in Dictionary) - //{ - // foreach (var component in item.Value) - // { - // output.Add - // ( - // string.Empty.PadRight(2) + - // I18N.Translate("webexpress:pluginmanager.plugin", item.Key.PluginId) - // ); - - // component.ComponentInstance?.PrepareForLog(item.Key, output, 4); - // } - //} - - HttpServerContext.Log.Info(string.Join(Environment.NewLine, output)); + _httpServerContext.Log.Info(string.Join(Environment.NewLine, output)); } /// @@ -506,6 +486,7 @@ internal void LogStatus() /// public void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 6f5dfa8..929bbe8 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -33,11 +33,6 @@ public interface IComponentHub : IComponentManager /// event EventHandler RemoveComponent; - /// - /// Returns the reference to the context of the host. - /// - IHttpServerContext HttpServerContext { get; } - /// /// Returns all registered components. /// @@ -47,19 +42,20 @@ public interface IComponentHub : IComponentManager /// Returns the log manager. /// /// The instance of the log manager. - LogManager LogManager { get; } + ILogManager LogManager { get; } /// /// Returns the package manager. /// /// The instance of the package manager. - PackageManager PackageManager { get; } + IPackageManager PackageManager { get; } /// /// Returns the plugin manager. /// /// The instance of the plugin manager. public IPluginManager PluginManager { get; } + /// /// Returns the application manager. /// diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 1a49807..c541236 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -35,7 +35,7 @@ public sealed class EndpointManager : IEndpointManager, ISystemComponent /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 4b4e2bf..378987b 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -196,7 +196,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// The associated component hub. + /// The reference to the context of the host. /// The associated plugin context. /// The corresponding application context. /// The event handler context. /// The event handler type. /// The event class. - public EventItem(IComponentHub componentHub, IPluginContext pluginContext, IApplicationContext applicationContext, IEventHandlerContext eventHandlerContext, Type eventHandlerType, Type eventClass) + public EventItem(IComponentHub componentHub, IHttpServerContext httpServerContext, IPluginContext pluginContext, IApplicationContext applicationContext, IEventHandlerContext eventHandlerContext, Type eventHandlerType, Type eventClass) { _componentHub = componentHub; PluginContext = pluginContext; @@ -58,6 +59,7 @@ public EventItem(IComponentHub componentHub, IPluginContext pluginContext, IAppl ( eventHandlerType, eventHandlerContext, + httpServerContext, _componentHub, pluginContext, applicationContext @@ -70,6 +72,7 @@ public EventItem(IComponentHub componentHub, IPluginContext pluginContext, IAppl ( eventHandlerType, eventHandlerContext, + httpServerContext, _componentHub, pluginContext, applicationContext diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 33b8fee..5bb85dc 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -47,11 +47,11 @@ public sealed class JobManager : IJobManager, ISystemComponent, IExecutableEleme /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private JobManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private JobManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentHub = componentManager; + _componentHub = componentHub; _componentHub.PluginManager.AddPlugin += OnAddPlugin; _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; @@ -84,9 +84,9 @@ private IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob Cron = cron }; - var item = new ScheduleItem(_componentHub, pluginContext, null, jobContext, typeof(T)); + var item = new ScheduleItem(_componentHub, _httpServerContext, pluginContext, null, jobContext, typeof(T)); - _dynamicScheduleList.Append(item); + _ = _dynamicScheduleList.Append(item); OnAddJob(jobContext); @@ -175,7 +175,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.Values) + .SelectMany(x => x)) + { + OnRemoveJob(scheduleItem.JobContext); + scheduleItem.Dispose(); + } - foreach (var scheduleItem in _staticScheduleDictionary[pluginContext].Values - .SelectMany(x => x.Values) - .SelectMany(x => x)) - { - OnRemoveJob(scheduleItem.JobContext); - scheduleItem.Dispose(); + _staticScheduleDictionary.Remove(pluginContext); } - - _staticScheduleDictionary.Remove(pluginContext); } /// diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs index ce189f1..296f310 100644 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleItem.cs @@ -11,8 +11,6 @@ namespace WebExpress.WebCore.WebJob.Model /// internal class ScheduleItem : IDisposable { - private readonly IComponentHub _componentHub; - /// /// Returns the associated plugin context. /// @@ -47,13 +45,13 @@ internal class ScheduleItem : IDisposable /// Initializes a new instance of the class. /// /// The associated component hub. + /// The reference to the context of the host. /// The associated plugin context. /// The corresponding application context. /// The job context. /// The job class. - public ScheduleItem(IComponentHub componentHub, IPluginContext pluginContext, IApplicationContext applicationContext, IJobContext jobContext, Type jobClass) + public ScheduleItem(IComponentHub componentHub, IHttpServerContext httpServerContext, IPluginContext pluginContext, IApplicationContext applicationContext, IJobContext jobContext, Type jobClass) { - _componentHub = componentHub; PluginContext = pluginContext; ApplicationContext = applicationContext; JobContext = jobContext; @@ -63,7 +61,8 @@ public ScheduleItem(IComponentHub componentHub, IPluginContext pluginContext, IA ( jobClass, jobContext, - _componentHub, + httpServerContext, + componentHub, pluginContext, applicationContext ); diff --git a/src/WebExpress.WebCore/WebLog/ILogManager.cs b/src/WebExpress.WebCore/WebLog/ILogManager.cs new file mode 100644 index 0000000..1f93427 --- /dev/null +++ b/src/WebExpress.WebCore/WebLog/ILogManager.cs @@ -0,0 +1,27 @@ +using System; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebLog +{ + /// + /// Interface for managing logs within the web application. + /// + public interface ILogManager : IComponentManager + { + /// + /// An event that fires when a log is added. + /// + event EventHandler AddLog; + + /// + /// An event that fires when a log is removed. + /// + event EventHandler RemoveLog; + + /// + /// Returns the default log. + /// + ILog DefaultLog { get; } + } +} diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 49547bf..2c4a236 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -1,50 +1,48 @@ using System; -using System.Collections.Generic; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebLog { - public class LogManager : IComponentManagerPlugin, ISystemComponent + /// + /// Manages logging operations and integrates with the system components. + /// + public class LogManager : ILogManager, ISystemComponent { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + /// - /// An event that fires when an log is added. + /// An event that fires when a log is added. /// public event EventHandler AddLog; /// - /// An event that fires when an log is removed. + /// An event that fires when a log is removed. /// public event EventHandler RemoveLog; - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } - /// /// Returns the default log. /// - public ILog DefaultLog => HttpServerContext.Log; + public ILog DefaultLog => _httpServerContext.Log; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - internal LogManager() + /// The component hub. + /// The reference to the context of the host. + private LogManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - } + _componentHub = componentHub; + _httpServerContext = httpServerContext; - /// - /// Initialization - /// - /// The reference to the context of the host. - public void Initialization(IHttpServerContext context) - { - HttpServerContext = context; + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:logmanager.initialization") ); @@ -54,18 +52,9 @@ public void Initialization(IHttpServerContext context) /// Discovers and registers logs from the specified plugin. /// /// A context of a plugin whose logs are to be registered. - public void Register(IPluginContext pluginContext) + private void Register(IPluginContext pluginContext) { - throw new System.NotImplementedException(); - } - /// - /// Discovers and registers logs from the specified plugin. - /// - /// A list with plugin contexts that contain the logs. - public void Register(IEnumerable pluginContexts) - { - throw new System.NotImplementedException(); } /// @@ -74,40 +63,44 @@ public void Register(IEnumerable pluginContexts) /// The context of the plugin that contains the log to remove. public void Remove(IPluginContext pluginContext) { - throw new System.NotImplementedException(); } /// /// Raises the AddLog event. /// - /// The plugin context. - private void OnAddModule(IPluginContext pluginContext) + /// The page context. + private void OnAddLog(IPluginContext resourceContext) { - AddLog?.Invoke(this, pluginContext); + AddLog?.Invoke(this, resourceContext); } /// /// Raises the RemoveLog event. /// - /// The plugin context. - private void OnRemoveModule(IPluginContext pluginContext) + /// The page context. + private void OnRemoveLog(IPluginContext pluginContext) { RemoveLog?.Invoke(this, pluginContext); } /// - /// Information about the component is collected and prepared for output in the log. + /// Handles the event when an plugin is added. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate("webexpress:logmanager.titel") - ); + Register(e); + } + + /// + /// Handles the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); } /// @@ -115,6 +108,10 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in /// public void Dispose() { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebPackage/IPackageManager.cs b/src/WebExpress.WebCore/WebPackage/IPackageManager.cs new file mode 100644 index 0000000..315e6d6 --- /dev/null +++ b/src/WebExpress.WebCore/WebPackage/IPackageManager.cs @@ -0,0 +1,27 @@ +using System; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPackage.Model; + +namespace WebExpress.WebCore.WebPackage +{ + /// + /// The package manager manages packages with WebExpress extensions. The packages must be in WebExpressPackage format (*.wxp). + /// + public interface IPackageManager : IComponentManager + { + /// + /// An event that fires when an package is added. + /// + event EventHandler AddPackage; + + /// + /// An event that fires when an package is removed. + /// + event EventHandler RemovePackage; + + /// + /// Returns the catalog of installed packages. + /// + PackageCatalog Catalog { get; } + } +} diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index a034131..6e45341 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -20,25 +20,21 @@ namespace WebExpress.WebCore.WebPackage /// /// The package manager manages packages with WebExpress extensions. The packages must be in WebExpressPackage format (*.wxp). /// - public sealed class PackageManager : IComponentManager, ISystemComponent + public sealed class PackageManager : IPackageManager, ISystemComponent { - private readonly ComponentHub _componentManager; + private readonly ComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; private readonly PluginManager _pluginManager; /// /// An event that fires when an package is added. /// - public static event EventHandler AddPackage; + public event EventHandler AddPackage; /// /// An event that fires when an package is removed. /// - public static event EventHandler RemovePackage; - - /// - /// Returns or sets the reference to the context of the host. - /// - public IHttpServerContext HttpServerContext { get; private set; } + public event EventHandler RemovePackage; /// /// Thread Termination. @@ -48,22 +44,22 @@ public sealed class PackageManager : IComponentManager, ISystemComponent /// /// Returns the catalog of installed packages. /// - private PackageCatalog Catalog { get; } = new PackageCatalog(); + public PackageCatalog Catalog { get; } = new PackageCatalog(); /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The plugin manager. - /// The reference to the context of the host. - private PackageManager(IComponentHub componentManager, IPluginManager pluginManager, IHttpServerContext context) + /// The reference to the context of the host. + private PackageManager(IComponentHub componentHub, IPluginManager pluginManager, IHttpServerContext httpServerContext) { - _componentManager = componentManager as ComponentHub; + _componentHub = componentHub as ComponentHub; _pluginManager = pluginManager as PluginManager; - HttpServerContext = context; + _httpServerContext = httpServerContext; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:packagemanager.initialization") ); @@ -78,17 +74,17 @@ internal void Execute() _pluginManager.Register(); // boot default elements - _componentManager.BootComponent(_pluginManager.Plugins); + _componentHub.BootComponent(_pluginManager.Plugins); LoadCatalog(); foreach (var package in Catalog.Packages) { - var packagesFromFile = LoadPackage(Path.Combine(HttpServerContext.PackagePath, package.File)); + var packagesFromFile = LoadPackage(Path.Combine(_httpServerContext.PackagePath, package.File)); package.Metadata = packagesFromFile?.Metadata; - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:packagemanager.existing", package.File) ); @@ -105,7 +101,7 @@ internal void Execute() SaveCatalog(); // build sitemap - _componentManager.SitemapManager.Refresh(); + _componentHub.SitemapManager.Refresh(); Task.Factory.StartNew(() => { @@ -133,17 +129,17 @@ public void ShutDown() /// public void Scan() { - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate ( "webexpress:packagemanager.scan", - HttpServerContext.PackagePath + _httpServerContext.PackagePath ) ); // determine all WebExpress packages from the file system - var packageFiles = Directory.GetFiles(HttpServerContext.PackagePath, "*.wxp").Select(x => Path.GetFileName(x)).ToList(); + var packageFiles = Directory.GetFiles(_httpServerContext.PackagePath, "*.wxp").Select(x => Path.GetFileName(x)).ToList(); // all packages that are not yet installed var newPackages = packageFiles.Except(Catalog.Packages.Where(x => x != null).Select(x => x.File)).ToList(); @@ -156,7 +152,7 @@ public void Scan() foreach (var package in newPackages) { - var packagesFromFile = LoadPackage(Path.Combine(HttpServerContext.PackagePath, package)); + var packagesFromFile = LoadPackage(Path.Combine(_httpServerContext.PackagePath, package)); ExtractPackage(packagesFromFile); RegisterPackage(packagesFromFile); @@ -164,7 +160,7 @@ public void Scan() Catalog.Packages.Add(packagesFromFile); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate ( @@ -176,11 +172,11 @@ public void Scan() foreach (var package in removePackages) { - var packagesFromFile = LoadPackage(Path.Combine(HttpServerContext.PackagePath, package)); + var packagesFromFile = LoadPackage(Path.Combine(_httpServerContext.PackagePath, package)); Catalog.Packages.Add(packagesFromFile); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate ( @@ -221,10 +217,10 @@ public void Scan() // } //} - if (newPackages.Any() || removePackages.Any()) + if (newPackages.Count != 0 || removePackages.Count != 0) { // build sitemap - _componentManager.SitemapManager.Refresh(); + _componentHub.SitemapManager.Refresh(); // save the catalog SaveCatalog(); @@ -293,10 +289,10 @@ private PackageCatalogItem LoadPackage(string file) } catch (Exception ex) { - HttpServerContext.Log.Exception(ex); + _httpServerContext.Log.Exception(ex); } - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate ( @@ -313,7 +309,7 @@ private PackageCatalogItem LoadPackage(string file) /// private void LoadCatalog() { - var catalogeFile = Path.Combine(HttpServerContext.PackagePath, "catalog.xml"); + var catalogeFile = Path.Combine(_httpServerContext.PackagePath, "catalog.xml"); if (File.Exists(catalogeFile)) { using var catalog = new StreamReader(catalogeFile); @@ -331,16 +327,16 @@ private void LoadCatalog() /// private void SaveCatalog() { - var catalogeFile = Path.Combine(HttpServerContext.PackagePath, "catalog.xml"); + var catalogeFile = Path.Combine(_httpServerContext.PackagePath, "catalog.xml"); using var fs = new FileStream(catalogeFile, FileMode.Create); using var writer = new XmlTextWriter(fs, Encoding.Unicode); var serializer = new XmlSerializer(typeof(PackageCatalog)); writer.Formatting = Formatting.Indented; - serializer.Serialize(writer, Catalog, new XmlSerializerNamespaces(new[] { new XmlQualifiedName("", "") })); + serializer.Serialize(writer, Catalog, new XmlSerializerNamespaces([new XmlQualifiedName("", "")])); - HttpServerContext.Log.Debug + _httpServerContext.Log.Debug ( I18N.Translate("webexpress:packagemanager.save") ); @@ -352,14 +348,14 @@ private void SaveCatalog() /// The package. private void ExtractPackage(PackageCatalogItem package) { - var packageFile = Path.Combine(HttpServerContext.PackagePath, package?.File); + var packageFile = Path.Combine(_httpServerContext.PackagePath, package?.File); if (File.Exists(packageFile)) { using var zip = ZipFile.Open(packageFile, ZipArchiveMode.Read); var specEntry = zip.Entries.Where(x => Path.GetExtension(x.FullName) == ".spec").FirstOrDefault(); - var extractedPath = Path.Combine(HttpServerContext.PackagePath, Path.GetFileNameWithoutExtension(package?.File)); + var extractedPath = Path.Combine(_httpServerContext.PackagePath, Path.GetFileNameWithoutExtension(package?.File)); if (!Directory.Exists(extractedPath)) { @@ -372,7 +368,7 @@ private void ExtractPackage(PackageCatalogItem package) { var entryFileName = Path.Combine(extractedPath, entry?.FullName); - if (entryFileName.EndsWith("/")) + if (entryFileName.EndsWith('/')) { if (!Directory.Exists(entryFileName)) { @@ -402,14 +398,12 @@ private void ExtractPackage(PackageCatalogItem package) private void RegisterPackage(PackageCatalogItem package) { // load plugins - foreach (var plugin in package?.Metadata.PluginSources ?? Enumerable.Empty()) + foreach (var plugin in package?.Metadata.PluginSources ?? []) { var pluginContexts = _pluginManager.Register(GetTargetPath(package, plugin)); package.Plugins.AddRange(pluginContexts); } - - _componentManager.LogStatus(); } /// @@ -418,7 +412,7 @@ private void RegisterPackage(PackageCatalogItem package) /// The package. private void BootPackage(PackageCatalogItem package) { - _componentManager.BootComponent(package.Plugins); + _componentHub.BootComponent(package.Plugins); } /// @@ -431,7 +425,7 @@ private string GetTargetPath(PackageCatalogItem package, string plugin) { return Path.GetFullPath(Path.Combine ( - HttpServerContext.PackagePath, + _httpServerContext.PackagePath, Path.GetFileNameWithoutExtension(package?.File), plugin, GetTFM(), $"{Path.GetFileName(plugin)}.dll" )); } @@ -440,7 +434,7 @@ private string GetTargetPath(PackageCatalogItem package, string plugin) /// Determines the target framework. /// /// The TFM - private string GetTFM() + private static string GetTFM() { var targetFrameworkAttribute = Assembly.GetExecutingAssembly() .GetCustomAttributes(typeof(TargetFrameworkAttribute), false) diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 5f82c9e..2d05e92 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -46,11 +46,11 @@ public class PageManager : IPageManager /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private PageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private PageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentHub = componentManager; + _componentHub = componentHub; _componentHub.PluginManager.AddPlugin += OnAddPlugin; _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; @@ -224,7 +224,7 @@ private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _componentHub); + var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _httpServerContext, _componentHub); if (instance is II18N i18n) { @@ -404,19 +404,17 @@ public void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) { - return; - } + foreach (var resourceItem in value.Values + .SelectMany(x => x.Values)) + { + OnRemovePage(resourceItem.PageContext); + resourceItem.Dispose(); + } - foreach (var resourceItem in _dictionary[pluginContext].Values - .SelectMany(x => x.Values)) - { - OnRemovePage(resourceItem.PageContext); - resourceItem.Dispose(); + _dictionary.Remove(pluginContext); } - - _dictionary.Remove(pluginContext); } /// @@ -480,16 +478,6 @@ private void OnRemovePage(IPageContext pageContext) RemovePage?.Invoke(this, pageContext); } - /// - /// Handles the event when an application is removed. - /// - /// The source of the event. - /// The context of the application being removed. - private void OnRemoveApplication(object sender, IApplicationContext e) - { - Remove(e); - } - /// /// Handles the event when an plugin is added. /// @@ -520,6 +508,16 @@ private void OnAddApplication(object sender, IApplicationContext e) Register(e); } + /// + /// Handles the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + /// /// Information about the component is collected and prepared for output in the log. /// @@ -552,6 +550,8 @@ public void Dispose() _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; _componentHub.ApplicationManager.AddApplication -= OnAddApplication; _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index c8097f7..263fda9 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -19,7 +19,7 @@ namespace WebExpress.WebCore.WebPlugin /// public sealed class PluginManager : IPluginManager, IExecutableElements, ISystemComponent { - private readonly IComponentHub _componentManager; + private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly PluginDictionary _dictionary = []; private readonly PluginDictionary _unfulfilledDependencies = []; @@ -42,11 +42,11 @@ public sealed class PluginManager : IPluginManager, IExecutableElements, ISystem /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private PluginManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private PluginManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentManager = componentManager; + _componentHub = componentHub; _httpServerContext = httpServerContext; @@ -98,7 +98,7 @@ internal void Register() Register(assembly); } - Logging(); + Log(); } /// @@ -148,7 +148,7 @@ internal IEnumerable Register(string pluginFile) pluginContexts.AddRange(pluginContext); } - Logging(); + Log(); return pluginContexts; } @@ -203,7 +203,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex } } - if (!applicationTypes.Any()) + if (applicationTypes.Count == 0) { // no application specified _httpServerContext.Log.Warning @@ -236,7 +236,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginLoadContext = loadContext, PluginClass = type, PluginContext = pluginContext, - Plugin = ComponentActivator.CreateInstance(type, pluginContext, _componentManager), + Plugin = ComponentActivator.CreateInstance(type, pluginContext, _httpServerContext, _componentHub), Dependencies = dependencies }); } @@ -247,7 +247,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginLoadContext = loadContext, PluginClass = type, PluginContext = pluginContext, - Plugin = ComponentActivator.CreateInstance(type, pluginContext, _componentManager), + Plugin = ComponentActivator.CreateInstance(type, pluginContext, _httpServerContext, _componentHub), Dependencies = dependencies, ApplicationTypes = applicationTypes }); @@ -269,7 +269,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex ); } - if (plugins.Any()) + if (plugins.Count != 0) { plugins.Add(pluginContext); } @@ -422,7 +422,7 @@ public IEnumerable GetPlugins(IApplicationContext applicationCon { return _dictionary.Values .Where(x => x.ApplicationTypes != null) - .Where(x => x.ApplicationTypes.Select(x => _componentManager.ApplicationManager.GetApplications(x)) + .Where(x => x.ApplicationTypes.Select(x => _componentHub.ApplicationManager.GetApplications(x)) .SelectMany(x => x) .Where(x => x.ApplicationId == applicationContext.ApplicationId) .Any()) @@ -444,7 +444,7 @@ public IEnumerable GetAssociatedApplications(IPluginContext } return pluginItem.ApplicationTypes? - .Select(x => _componentManager.ApplicationManager.GetApplications(x)) + .Select(x => _componentHub.ApplicationManager.GetApplications(x)) .SelectMany(x => x) .Where(x => x != null) ?? []; } @@ -585,7 +585,7 @@ private void OnRemovePlugin(IPluginContext pluginContext) /// /// Output of the loaded plugins to the log. /// - private void Logging() + private void Log() { using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List(); @@ -637,16 +637,6 @@ private void Logging() } } - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 07295cc..b3d39a1 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -45,7 +45,7 @@ public sealed class ResourceManager : IResourceManager, ISystemComponent /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { @@ -214,19 +214,17 @@ internal void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) { - return; - } + foreach (var resourceItem in value.Values + .SelectMany(x => x.Values)) + { + OnRemoveResource(resourceItem.ResourceContext); + resourceItem.Dispose(); + } - foreach (var resourceItem in _dictionary[pluginContext].Values - .SelectMany(x => x.Values)) - { - OnRemoveResource(resourceItem.ResourceContext); - resourceItem.Dispose(); + _dictionary.Remove(pluginContext); } - - _dictionary.Remove(pluginContext); } /// @@ -395,7 +393,7 @@ private IResource CreateResourceInstance(IResourceContext resourceContext, Cultu if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _componentHub); + var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _httpServerContext, _componentHub); if (instance is II18N i18n) { diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 342da79..a791910 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -243,7 +243,7 @@ private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo cul if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _componentHub); + var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _httpServerContext, _componentHub); if (instance is II18N i18n) { @@ -421,19 +421,17 @@ public void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (!_dictionary.ContainsKey(pluginContext)) + if (_dictionary.TryGetValue(pluginContext, out var value)) { - return; - } + foreach (var resourceItem in value.Values + .SelectMany(x => x.Values)) + { + OnRemoveRestApi(resourceItem.RestApiContext); + resourceItem.Dispose(); + } - foreach (var resourceItem in _dictionary[pluginContext].Values - .SelectMany(x => x.Values)) - { - OnRemoveRestApi(resourceItem.RestApiContext); - resourceItem.Dispose(); + _dictionary.Remove(pluginContext); } - - _dictionary.Remove(pluginContext); } /// @@ -569,6 +567,8 @@ public void Dispose() _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; _componentHub.ApplicationManager.AddApplication -= OnAddApplication; _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 72ce28a..37392ba 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -7,7 +7,6 @@ using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSitemap.Model; using WebExpress.WebCore.WebUri; @@ -95,10 +94,7 @@ public void Refresh() _root = newSiteMapNode; - using var frame = new LogFrameSimple(_httpServerContext.Log); - var list = new List(); - PrepareForLog(null, list, 2); - _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); + Log(); } /// @@ -113,7 +109,7 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) var result = SearchNode ( _root, - new Queue(requestUri.Segments.Select(x => x == "/" ? x : (x.EndsWith("/") ? x[..^1] : x))), + new Queue(requestUri.Segments.Select(x => x == "/" ? x : (x.EndsWith('/') ? x[..^1] : x))), new Queue(), searchContext ); @@ -241,7 +237,7 @@ private static SitemapNode CreateSiteMap SitemapNode parent ) { - var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + var pathSegment = contextPathSegments.Count != 0 ? contextPathSegments.Dequeue() : null; if (pathSegment == null) { @@ -254,7 +250,7 @@ SitemapNode parent Parent = parent, }; - if (contextPathSegments.Any()) + if (contextPathSegments.Count != 0) { node.Children.Add(CreateSiteMap(contextPathSegments, applicationContext, node)); } @@ -264,7 +260,7 @@ SitemapNode parent /// /// Creates the sitemap. Works recursively. - /// It is important for the algorithm that the addition of module is sorted + /// It is important for the algorithm that the addition is sorted /// by the number of path segments in ascending order. /// /// The path segments of the context path. @@ -303,7 +299,7 @@ private static SitemapNode CreateSiteMap SitemapNode parent = null ) { - var pathSegment = contextPathSegments.Any() ? contextPathSegments.Dequeue() : null; + var pathSegment = contextPathSegments.Count != 0 ? contextPathSegments.Dequeue() : null; if (pathSegment == null) { @@ -317,7 +313,7 @@ private static SitemapNode CreateSiteMap EndpointContext = endpointContext }; - if (contextPathSegments.Any()) + if (contextPathSegments.Count != 0) { node.Children.Add(CreateSiteMap(contextPathSegments, endpointContext, node)); } @@ -338,11 +334,7 @@ private static void MergeSitemap(SitemapNode first, SitemapNode second) { foreach (var fc in first.Children.Where(x => x.PathSegment.Equals(sc.PathSegment))) { - if (fc.EndpointContext == null) - { - fc.EndpointContext = sc.EndpointContext; - //fc.Parent = sc.Parent; - } + fc.EndpointContext ??= sc.EndpointContext; MergeSitemap(fc, sc); return; @@ -363,7 +355,7 @@ private static void MergeSitemap(SitemapNode first, SitemapNode second) /// The path segments. /// The search context. /// The search result with the found resource - private SearchResult SearchNode + private static SearchResult SearchNode ( SitemapNode node, Queue inPathSegments, @@ -439,18 +431,16 @@ private static bool IsMatched(SitemapNode node, string pathSegement) /// /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { - output.Add - ( + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { I18N.Translate ( "webexpress:sitemapmanager.sitemap" ) - ); + }; var preorder = _root .GetPreOrder() @@ -463,8 +453,10 @@ public void PrepareForLog(IPluginContext pluginContext, IList output, in foreach (var node in preorder) { - output.Add(node); + list.Add(node); } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 8c621af..d4bba91 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -45,11 +45,11 @@ public class StatusPageManager : IStatusPageManager, ISystemComponent /// /// Initializes a new instance of the class. /// - /// The component manager. + /// The component hub. /// The reference to the context of the host. - private StatusPageManager(IComponentHub componentManager, IHttpServerContext httpServerContext) + private StatusPageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentHub = componentManager; + _componentHub = componentHub; _componentHub.PluginManager.AddPlugin += OnAddPlugin; _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; @@ -288,25 +288,21 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon if (statusPageItem == null) { - switch (status) + return status switch { - case 400: - return new ResponseBadRequest(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); - case 401: - return new ResponseUnauthorized(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); - case 404: - return new ResponseNotFound(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); - case 500: - return new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); - default: - return new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null); - } + 400 => new ResponseBadRequest(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null), + 401 => new ResponseUnauthorized(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null), + 404 => new ResponseNotFound(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null), + 500 => new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null), + _ => new ResponseInternalServerError(!string.IsNullOrWhiteSpace(message) ? new StatusMessage(message) : null), + }; } var instance = ComponentActivator.CreateInstance ( statusPageItem.StatusPageClass, statusPageItem.StatusPageContext, + _httpServerContext, _componentHub, new StatusMessage(message) ); @@ -328,7 +324,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon instance.Process(renderContext); - var response = ComponentActivator.CreateInstance(statusPageItem.StatusResponse, _componentHub, new StatusMessage(message)); + var response = ComponentActivator.CreateInstance(statusPageItem.StatusResponse, _httpServerContext, _componentHub, new StatusMessage(message)); var content = renderContext.VisualTree.Render(new VisualTreeContext(request))?.ToString(); response.Content = content; @@ -471,6 +467,8 @@ public void Dispose() _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; _componentHub.ApplicationManager.AddApplication -= OnAddApplication; _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 028b125..8075cfb 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -71,15 +71,15 @@ public ITask CreateTask(string id, params object[] args) { var key = id?.ToLower(); - if (!_dictionary.ContainsKey(id)) + if (_dictionary.TryGetValue(id, out var value)) { - var task = ComponentActivator.CreateInstance(_componentHub, [id, args]); - _dictionary.Add(key, task); - - return task; + return value; } - return _dictionary[id]; + var task = ComponentActivator.CreateInstance(_httpServerContext, _componentHub, [id, args]); + _dictionary.Add(key, task); + + return task; } /// @@ -105,17 +105,17 @@ public ITask CreateTask(string id, EventHandler handler, param { var key = id?.ToLower(); - if (!_dictionary.ContainsKey(id)) + if (_dictionary.TryGetValue(id, out var value)) { - var task = ComponentActivator.CreateInstance(_componentHub, [id, args]); - _dictionary.Add(key, task); + return value; + } - task.Process += handler; + var task = ComponentActivator.CreateInstance(_httpServerContext, _componentHub, [id, args]); + _dictionary.Add(key, task); - return task; - } + task.Process += handler; - return _dictionary[id]; + return task; } /// @@ -134,6 +134,7 @@ public void RemoveTask(ITask task) /// public void Dispose() { + GC.SuppressFinalize(this); } } } From 674417ff021dd5450b2233e139506f373693e21f Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 27 Oct 2024 22:40:40 +0100 Subject: [PATCH 037/162] move setting page manager from webexpress.ui to webexpress.core --- .../Manager/UnitTestSettingPageManager.cs | 145 ++++++ .../WebApplication/ApplicationManager.cs | 4 +- .../WebAttribute/SettingContextAttribute.cs | 20 + .../WebAttribute/SettingGroupAttribute.cs | 20 + .../WebAttribute/SettingHideAttribute.cs | 16 + .../WebAttribute/SettingSectionAttribute.cs | 21 + .../WebComponent/ComponentHub.cs | 12 +- .../WebComponent/IComponentHub.cs | 9 +- .../WebEndpoint/EndpointManager.cs | 4 +- .../WebEvent/EventManager.cs | 16 +- src/WebExpress.WebCore/WebEx.cs | 2 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 8 +- src/WebExpress.WebCore/WebLog/LogManager.cs | 4 +- .../WebPage/Model/PageDictionary.cs | 2 +- .../WebPage/Model/PageItem.cs | 6 - src/WebExpress.WebCore/WebPage/PageManager.cs | 17 +- .../WebResource/ResourceManager.cs | 12 +- .../WebRestAPI/RestApiManager.cs | 12 +- .../WebSettingPage/ISettingPage.cs | 32 ++ .../WebSettingPage/ISettingPageContext.cs | 32 ++ .../WebSettingPage/ISettingPageManager.cs | 27 ++ .../Model/SettingPageDictionary.cs | 48 ++ .../Model/SettingPageDictionaryItemContext.cs | 63 +++ .../Model/SettingPageDictionaryItemGroup.cs | 83 ++++ .../Model/SettingPageDictionaryItemSection.cs | 102 ++++ .../WebSettingPage/Model/SettingPageItem.cs | 65 +++ .../WebSettingPage/Model/SettingSection.cs | 23 + .../WebSettingPage/Model/TimeSpanConverter.cs | 52 +++ .../WebSettingPage/SettingPageContext.cs | 88 ++++ .../WebSettingPage/SettingPageManager.cs | 434 ++++++++++++++++++ .../WebSettingPage/SettingPageSearchResult.cs | 30 ++ .../WebStatusPage/StatusPageManager.cs | 12 +- 32 files changed, 1366 insertions(+), 55 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/SettingHideAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/TimeSpanConverter.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/SettingPageSearchResult.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs new file mode 100644 index 0000000..0963405 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -0,0 +1,145 @@ +using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the setting page manager. + /// + [Collection("NonParallelTests")] + public class UnitTestSettingPageManager + { + /// + /// Test the register function of the setting page manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(0, componentHub.SettingPageManager.SettingPages.Count()); + } + + /// + /// Test the remove function of the setting page manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var settingPageManager = componentHub.SettingPageManager as SettingPageManager; + + // test execution + settingPageManager.Remove(plugin); + + Assert.Empty(componentHub.SettingPageManager.SettingPages); + } + + ///// + ///// Test the id property of the setting page. + ///// + //[Theory] + //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "webexpress.webcore.test.testpagea")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "webexpress.webcore.test.testpageb")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "webexpress.webcore.test.testpagec")] + //public void Id(Type applicationType, Type resourceType, string id) + //{ + // // preconditions + // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); + + // // test execution + // Assert.Equal(id, settingPage.EndpointId); + //} + + ///// + ///// Test the title property of the setting page. + ///// + //[Theory] + //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "webindex:pagea.label")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "webindex:pageb.label")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "webindex:pagec.label")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "webindex:pagea.label")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "webindex:pageb.label")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "webindex:pagec.label")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "webindex:pagea.label")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "webindex:pageb.label")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "webindex:pagec.label")] + + //public void Title(Type applicationType, Type resourceType, string id) + //{ + // // preconditions + // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); + + // // test execution + // Assert.Equal(id, settingPage.PageTitle); + //} + + ///// + ///// Test the context path property of the setting page. + ///// + //[Theory] + //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "/appa")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "/appa/resa")] + //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "/appa")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "/appb")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "/appb/resa")] + //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "/appb")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "/")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "/resa")] + //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "/")] + //public void ContextPath(Type applicationType, Type resourceType, string id) + //{ + // // preconditions + // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); + + // // test execution + // Assert.Equal(id, settingPage.ContextPath); + //} + + /// + /// Tests whether the setting page manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SettingPageManager.GetType())); + } + + /// + /// Tests whether the setting page context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + foreach (var settingPages in componentHub.SettingPageManager.SettingPages) + { + Assert.True(typeof(IContext).IsAssignableFrom(settingPages.GetType()), $"Page context {settingPages.GetType().Name} does not implement IContext."); + } + } + } +} diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 18c421b..705a6a2 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -378,7 +378,7 @@ private void OnRemoveApplication(IApplicationContext applicationContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -388,7 +388,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. diff --git a/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs new file mode 100644 index 0000000..c7616da --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify the context in which the settings page is associated. + /// + [AttributeUsage(AttributeTargets.Class)] + public class SettingContextAttribute : Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The context in which the settings page is associated. + public SettingContextAttribute(string context) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs new file mode 100644 index 0000000..8f588a0 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to define a setting group for identity. + /// + [AttributeUsage(AttributeTargets.Class)] + public class SettingGroupAttribute : System.Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The group name. + public SettingGroupAttribute(string name) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/SettingHideAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingHideAttribute.cs new file mode 100644 index 0000000..837fa1c --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SettingHideAttribute.cs @@ -0,0 +1,16 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Represents an attribute that hides a setting in the web UI. + /// + public class SettingHideAttribute : System.Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + public SettingHideAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs new file mode 100644 index 0000000..78afe5d --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs @@ -0,0 +1,21 @@ +using System; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify the section where the settings page is listed. + /// + [AttributeUsage(AttributeTargets.Class)] + public class SettingSectionAttribute : Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The section where the settings page is listed. + public SettingSectionAttribute(SettingSection section) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 5e6b305..1b44825 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.SettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent.Model; using WebExpress.WebCore.WebEndpoint; @@ -35,6 +36,7 @@ public class ComponentHub : IComponentHub private readonly EndpointManager _endpointManager; private readonly ResourceManager _resourceManager; private readonly PageManager _pageManager; + private readonly SettingPageManager _settingPageManager; private readonly RestApiManager _restApiManager; private readonly SitemapManager _sitemapManager; private readonly StatusPageManager _statusPageManager; @@ -66,6 +68,7 @@ public class ComponentHub : IComponentHub _sitemapManager, _resourceManager, _pageManager, + _settingPageManager, _restApiManager, _eventManager, _jobManager, @@ -135,6 +138,12 @@ public class ComponentHub : IComponentHub /// The instance of the page manager. public IPageManager PageManager => _pageManager; + /// + /// Returns the setting page manager. + /// + /// The instance of the setting page manager. + public ISettingPageManager SettingPageManager => _settingPageManager; + /// /// Returns the rest api manager. /// @@ -184,6 +193,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _endpointManager = CreateInstance(typeof(EndpointManager)) as EndpointManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; _pageManager = CreateInstance(typeof(PageManager)) as PageManager; + _settingPageManager = CreateInstance(typeof(SettingPageManager)) as SettingPageManager; _restApiManager = CreateInstance(typeof(RestApiManager)) as RestApiManager; _statusPageManager = CreateInstance(typeof(StatusPageManager)) as StatusPageManager; _eventManager = CreateInstance(typeof(EventManager)) as EventManager; @@ -251,7 +261,7 @@ private IComponentManager CreateInstance(Type componentType) /// /// The id. /// The instance of the component or null. - public IComponentManager GetComponent(string id) + public IComponentManager GetComponentManager(string id) { return _dictionary.Values .SelectMany(x => x) diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 929bbe8..d74b1af 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.SettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; @@ -98,6 +99,12 @@ public interface IComponentHub : IComponentManager /// The instance of the page manager. IPageManager PageManager { get; } + /// + /// Returns the setting page manager. + /// + /// The instance of the setting page manager. + ISettingPageManager SettingPageManager { get; } + /// /// Returns the rest api manager. /// @@ -133,7 +140,7 @@ public interface IComponentHub : IComponentManager /// /// The id. /// The instance of the component. - IComponentManager GetComponent(string id); + IComponentManager GetComponentManager(string id); /// /// Returns a component based on its type. diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index c541236..5adfbc7 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -107,7 +107,7 @@ public Response HandleRequest(Request request, IEndpointContext endpointContext) } /// - /// Handles the event when an endpoint is added. + /// Raises the event when an endpoint is added. /// /// The source of the event. /// The context of the endpoint being added. @@ -117,7 +117,7 @@ private void OnAddEndpoint(object sender, IEndpointContext endpointContext) } /// - /// Handles the event when an endpoint is removed. + /// Raises the event when an endpoint is removed. /// /// The source of the event. /// The context of the endpoint being removed. diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 378987b..e664630 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -228,9 +228,9 @@ private void Register(IPluginContext pluginContext, IEnumerable - /// Removes all jobs associated with the specified plugin context. + /// Removes all events associated with the specified plugin context. /// - /// The context of the plugin that contains the event to remove. + /// The context of the plugin that contains the events to remove. internal void Remove(IPluginContext pluginContext) { // the plugin has not been registered in the manager @@ -248,9 +248,9 @@ internal void Remove(IPluginContext pluginContext) } /// - /// Removes all jobs associated with the specified application context. + /// Removes all events associated with the specified application context. /// - /// The context of the application that contains the jobs to remove. + /// The context of the application that contains the events to remove. internal void Remove(IApplicationContext applicationContext) { if (applicationContext == null) @@ -292,7 +292,7 @@ private void OnRemoveEventHandler(IEventHandlerContext eventHandlerContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -302,7 +302,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -312,7 +312,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. @@ -322,7 +322,7 @@ private void OnRemoveApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 20613f2..9a217f5 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -297,7 +297,7 @@ private void Exit() /// The instance of the component or null. public static IComponentManager GetComponent(string id) { - return _componentHub.GetComponent(id); + return _componentHub.GetComponentManager(id); } /// diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 5bb85dc..f2605ce 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -298,7 +298,7 @@ private void OnRemoveJob(IJobContext jobContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -308,7 +308,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -318,7 +318,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. @@ -328,7 +328,7 @@ private void OnRemoveApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 2c4a236..1c2bdd5 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -84,7 +84,7 @@ private void OnRemoveLog(IPluginContext pluginContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -94,7 +94,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs index e6e9268..9149954 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.WebPage.Model /// /// Represents a dictionary that maps plugin contexts to application contexts, page types, and page items. /// key = plugin context - /// value = { key = page type, value = page item } + /// value = application context { key = page type, value = page item } /// internal class PageDictionary : Dictionary>> { diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 038a9ce..8fec312 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage.Model @@ -70,11 +69,6 @@ internal class PageItem : IDisposable /// public bool Optional { get; set; } - /// - /// Returns the log to write status messages to the console and to a log file. - /// - public ILog Log { get; internal set; } - /// /// Returns the page context. /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 2d05e92..f6ddb61 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -344,7 +344,7 @@ private void Register(IPluginContext pluginContext, IEnumerable - /// Removes all jobs associated with the specified application context. + /// Removes all pages associated with the specified application context. /// - /// The context of the application that contains the jobs to remove. + /// The context of the application that contains the page to remove. internal void Remove(IApplicationContext applicationContext) { if (applicationContext == null) @@ -479,7 +478,7 @@ private void OnRemovePage(IPageContext pageContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -489,7 +488,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -499,7 +498,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. @@ -509,7 +508,7 @@ private void OnAddApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index b3d39a1..783de30 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -228,9 +228,9 @@ internal void Remove(IPluginContext pluginContext) } /// - /// Removes all jobs associated with the specified application context. + /// Removes all resources associated with the specified application context. /// - /// The context of the application that contains the jobs to remove. + /// The context of the application that contains the resources to remove. internal void Remove(IApplicationContext applicationContext) { if (applicationContext == null) @@ -430,7 +430,7 @@ private void OnRemoveResource(IResourceContext resourceContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -440,7 +440,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -449,7 +449,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) Remove(e); } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. @@ -459,7 +459,7 @@ private void OnRemoveApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index a791910..055a0d2 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -435,9 +435,9 @@ public void Remove(IPluginContext pluginContext) } /// - /// Removes all jobs associated with the specified application context. + /// Removes all events associated with the specified application context. /// - /// The context of the application that contains the jobs to remove. + /// The context of the application that contains the events to remove. internal void Remove(IApplicationContext applicationContext) { if (applicationContext == null) @@ -496,7 +496,7 @@ private void OnRemoveRestApi(IRestApiContext pageContext) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -506,7 +506,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -516,7 +516,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. @@ -526,7 +526,7 @@ private void OnRemoveApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs new file mode 100644 index 0000000..5408e02 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs @@ -0,0 +1,32 @@ +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Defines the contract for a setting page resource. + /// + public interface ISettingPage : IEndpoint + { + /// + /// Processing of the page. + /// + /// The context for rendering the page. + void Process(IRenderContext context); + + /// + /// Redirect to another page. + /// The function throws the RedirectException. + /// + /// The uri to redirect to. + void Redirecting(string uri); + } + + /// + /// Defines the contract for a setting page resource that can be rendered using a specific context. + /// + /// The type of the render context. + public interface ISettingPage : IPage where T : IRenderContext + { + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs new file mode 100644 index 0000000..128463f --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -0,0 +1,32 @@ +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.SettingPage +{ + /// + /// Interface representing the context of a setting page. + /// Provides access to plugin context, application context, conditions for activation, and caching behavior. + /// + public interface ISettingPageContext : IEndpointContext + { + /// + /// Returns the group to which the setting page belongs. + /// + string Group { get; } + + /// + /// Returns the section to which the setting page belongs. + /// + SettingSection Section { get; } + + /// + /// Returns a value indicating whether the page should be displayed or hidden. + /// + bool Hide { get; } + + /// + /// Returns or sets the icon. + /// + string Icon { get; } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs new file mode 100644 index 0000000..421549b --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.SettingPage +{ + /// + /// Interface for managing setting pages. + /// + public interface ISettingPageManager : IComponentManager + { + /// + /// An event that fires when an setting page is added. + /// + event EventHandler AddSettingPage; + + /// + /// An event that fires when an setting page is removed. + /// + event EventHandler RemoveSettingPage; + + /// + /// Returns the collection of setting pages. + /// + IEnumerable SettingPages { get; } + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs new file mode 100644 index 0000000..d86a97d --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents a dictionary that maps plugin contexts to application contexts and their corresponding setting page dictionary item contexts. + /// + internal class SettingPageDictionary : Dictionary> + { + /// + /// Returns the collection of setting pages. + /// + public IEnumerable SettingPages => Values + .SelectMany(a => a.Values) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .Select(x => x.SettingPageContext); + + /// + /// Adds a settings page. + /// + /// The settings page to insert. + public bool AddSettingPageItem(SettingPageItem item) + { + if (!TryGetValue(item.PluginContext, out var appDict)) + { + appDict = []; + Add(item.PluginContext, appDict); + } + + if (!appDict.TryGetValue(item.ApplicationContext, out var contextDict)) + { + contextDict = []; + appDict.Add(item.ApplicationContext, contextDict); + } + + contextDict.AddPage(item.Context, item.Section, item.Group, item); + + return true; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs new file mode 100644 index 0000000..472c44b --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using WebExpress.WebCore.SettingPage; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + public class SettingPageDictionaryItemContext : Dictionary + { + /// + /// Adds a page. + /// + /// The settig context. + /// The section. + /// The group. + /// The item to insert. + public void AddPage(string context, SettingSection section, string group, SettingPageItem page) + { + context ??= "*"; + + // register context + if (!ContainsKey(context)) + { + Add(context, new SettingPageDictionaryItemSection()); + } + + this[context].AddPage(section, group, page); + } + + /// + /// Searches for a setting page by its id. + /// + /// The setting site. + /// The setting page found or null. + public SettingPageSearchResult FindPage(string pageId) + { + foreach (var v in this) + { + var path = v.Value.FindPage(pageId); + if (path != null) + { + path.Context = v.Key; + return path; + } + } + + return null; + } + + /// + /// Provide all sections that have the same setting context. + /// + /// The setting context. + /// A listing of all sections of the same context. + public SettingPageDictionaryItemSection GetSections(string context) + { + if (ContainsKey(context)) + { + return this[context]; + } + + return null; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs new file mode 100644 index 0000000..3fcf3f0 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.SettingPage; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + public class SettingPageDictionaryItemGroup : Dictionary> + { + /// + /// Adds a page. + /// + /// The group. + /// The item to insert. + public void AddPage(string group, SettingPageItem page) + { + group ??= string.Empty; + + // register group. + if (!ContainsKey(group)) + { + Add(group, new List()); + } + + this[group].Add(page); + } + + /// + /// Searches for an item based on its Id. + /// + /// The setting site. + /// The setting page found or null. + public SettingPageSearchResult FindPage(string pageId) + { + foreach (var v in this) + { + var item = v.Value.Where(x => x.SettingPageContext.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + if (item != null) + { + return new SettingPageSearchResult() { Group = v.Key, Item = item }; + } + } + + return null; + } + + /// + /// Returns the first setting page. + /// + /// The first setting page. + public SettingPageSearchResult FindFirstPage() + { + var firstItem = default(SettingPageItem); + + foreach (var group in this.OrderBy(x => x.Key)) + { + firstItem = group.Value.FirstOrDefault(); + + if (firstItem != null) + { + return new SettingPageSearchResult() { Group = group.Key, Item = firstItem }; + } + } + + return null; + } + + /// + /// Returns all setting pages that are in the given group. + /// + /// The group. + /// A listing of all pages in the same group. + public List GetPages(string group) + { + if (ContainsKey(group)) + { + return this[group]; + } + + return null; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs new file mode 100644 index 0000000..00c9dec --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using WebExpress.WebCore.SettingPage; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + public class SettingPageDictionaryItemSection : Dictionary + { + /// + /// Adds an item. + /// + /// The section. + /// The group. + /// The item to insert. + public void AddPage(SettingSection section, string group, SettingPageItem item) + { + // register Section + if (!ContainsKey(section)) + { + Add(section, new SettingPageDictionaryItemGroup()); + } + + this[section].AddPage(group, item); + } + + /// + /// Searches for an item based on its id. + /// + /// The setting site id. + /// The setting page found or null. + public SettingPageSearchResult FindPage(string pageId) + { + foreach (var v in this) + { + var path = v.Value.FindPage(pageId); + if (path != null) + { + path.Section = v.Key; + return path; + } + } + + return null; + } + + /// + /// Returns the first setting page. + /// + /// The first setting page. + public SettingPageSearchResult FindFirstPage() + { + var firstPage = default(SettingPageSearchResult); + + if (ContainsKey(SettingSection.Preferences)) + { + firstPage = this[SettingSection.Preferences].FindFirstPage(); + if (firstPage != null) + { + firstPage.Section = SettingSection.Preferences; + + return firstPage; + } + } + else if (ContainsKey(SettingSection.Primary)) + { + firstPage = this[SettingSection.Primary].FindFirstPage(); + if (firstPage != null) + { + firstPage.Section = SettingSection.Primary; + + return firstPage; + } + } + else if (ContainsKey(SettingSection.Secondary)) + { + firstPage = this[SettingSection.Secondary].FindFirstPage(); + if (firstPage != null) + { + firstPage.Section = SettingSection.Secondary; + + return firstPage; + } + } + + return firstPage; + } + + /// + /// Returns a groups that are in the given section. + /// + /// The section. + /// A group in the same section. + public SettingPageDictionaryItemGroup GetGroup(SettingSection section) + { + if (ContainsKey(section)) + { + return this[section]; + } + + return null; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs new file mode 100644 index 0000000..ade9af4 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -0,0 +1,65 @@ +using System; +using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents an item on the setting page. + /// + public class SettingPageItem : IDisposable + { + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the setting page context. + /// + public ISettingPageContext SettingPageContext { get; internal set; } + + /// + /// Returns or sets the class type of the setting page. + /// + public Type SettingPageClass { get; internal set; } + + /// + /// Returns or sets the instance of the setting page, if the page is cached, otherwise null. + /// + public ISettingPage Instance { get; internal set; } + + /// + /// Returns the setting context. + /// + public string Context { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Returns the group. + /// + public string Group { get; internal set; } + + /// + /// Returns a value indicating whether the component is created once and reused on each execution. + /// + public bool Cache { get; internal set; } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs new file mode 100644 index 0000000..20b6b88 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs @@ -0,0 +1,23 @@ +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Definition of keys for the identification of sections in Wen pages, which can be occupied by components. + /// + public enum SettingSection + { + /// + /// Returns the preference section. + /// + Preferences, + + /// + /// Returns the primary section. + /// + Primary, + + /// + /// Returns the secondary section. + /// + Secondary + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/TimeSpanConverter.cs b/src/WebExpress.WebCore/WebSettingPage/Model/TimeSpanConverter.cs new file mode 100644 index 0000000..47d58b7 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/TimeSpanConverter.cs @@ -0,0 +1,52 @@ +using System; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + + /// + /// Converts a TimeSpan object to a formatted string and vice versa. + /// + public class TimeSpanConverter + { + /// + /// Converts a TimeSpan object to a formatted string. + /// + /// The TimeSpan object to convert. + /// The type to convert to. + /// Optional parameter for conversion. + /// The language to use in the converter. + /// A formatted string representing the TimeSpan. + public object Convert(object value, Type targetType, object parameter, string language) + { + if (value == null) + { + return null; + } + + var ts = TimeSpan.Parse(value.ToString()); + return string.Format + ( + "{0}d {1:D2}h {2:D2}m {3:D2}s {4:D2}ms", + ts.Days, + ts.Hours, + ts.Minutes, + ts.Seconds, + ts.Milliseconds + ); + } + + /// + /// Converts a formatted string back to a TimeSpan object. + /// + /// The formatted string to convert. + /// The type to convert to. + /// Optional parameter for conversion. + /// The language to use in the converter. + /// The TimeSpan object. + /// Thrown when the method is not implemented. + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs new file mode 100644 index 0000000..9fe1cff --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage.Model; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.SettingPage +{ + /// + /// Interface representing the context of a setting page. + /// Provides access to plugin context, application context, conditions for activation, and caching behavior. + /// + public class SettingPageContext : ISettingPageContext + { + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the unique identifier for the setting page. + /// + public string EndpointId { get; internal set; } + + /// + /// Returns the group to which the setting page belongs. + /// + public string Group { get; internal set; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + public IEnumerable Conditions { get; internal set; } + + /// + /// Returns a value indicating whether the component is created once and reused on each execution. + /// + public bool Cache { get; internal set; } + + /// + /// Returns a value indicating whether the page should be displayed or hidden. + /// + public bool Hide { get; internal set; } + + /// + /// Returns the icon. + /// + public string Icon { get; internal set; } + + /// + /// Returns the setting context. + /// + public string Context { get; internal set; } + + /// + /// Returns the section of the setting page. + /// + public SettingSection Section { get; internal set; } + + /// + /// Returns the parent context of the endpoint. + /// + public IEndpointContext ParentContext { get; internal set; } + + /// + /// Returns a value indicating whether to include sub-paths. + /// + public bool IncludeSubPaths { get; internal set; } + + /// + /// Returns the context path of the URI resource. + /// + public UriResource ContextPath { get; internal set; } + + /// + /// Returns the URI of the setting page. + /// + public UriResource Uri { get; internal set; } + + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs new file mode 100644 index 0000000..d8f1454 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.SettingPage +{ + /// + /// Management of settings pages. + /// + public sealed class SettingPageManager : ISettingPageManager + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly SettingPageDictionary _dictionary = []; + + /// + /// An event that fires when an setting page is added. + /// + public event EventHandler AddSettingPage; + + /// + /// An event that fires when an setting page is removed. + /// + public event EventHandler RemoveSettingPage; + + /// + /// Returns the collection of setting pages. + /// + public IEnumerable SettingPages => _dictionary.SettingPages; + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The reference to the context of the host. + private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + _httpServerContext = httpServerContext; + + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; + + var endpointtRegistration = new EndpointRegistration() + { + EndpointResolver = (type, applicationContext) => applicationContext != null ? GetSettingPages(type, applicationContext) : GetSettingPages(type), + EndpointsResolver = () => SettingPages, + HandleRequest = (request, endpontContext) => + { + var settingPage = CreateSettingPageInstance(endpontContext as ISettingPageContext, request.Culture); + var settingPageType = settingPage.GetType(); + var context = default(IRenderContext); + var pageContetx = endpontContext as IPageContext; + + if (settingPageType.IsGenericType) + { + var typeOfT = settingPageType.GetGenericArguments()[0]; + var parameters = new object[] { settingPage, endpontContext as IPageContext, request }; + + context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; + } + else + { + context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); + } + + settingPage.Process(context); + + return new ResponseOK() + { + Content = context.VisualTree.Render(new VisualTreeContext(context)) + }; + } + }; + + AddSettingPage += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemoveSettingPage += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + + _componentHub.EndpointManager.Register(endpointtRegistration); + + _httpServerContext.Log.Debug(I18N.Translate("webexpress.webapp:pagesettingmanager.initialization")); + } + + /// + /// Creates a new setting page and returns it. If a page already exists (through caching), the existing instance is returned. + /// + /// The context used for setting page creation. + /// The culture with the language settings. + /// The created or cached page. + private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageContext, CultureInfo culture) + { + var settingPageItem = _dictionary.Values + .SelectMany(a => a.Values) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .FirstOrDefault(x => x.SettingPageContext.Equals(settinPageContext)); + + if (settingPageItem != null && settingPageItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance(settingPageItem.SettingPageClass, settinPageContext, _httpServerContext, _componentHub); + + if (instance is II18N i18n) + { + i18n.Culture = culture; + } + + if (settingPageItem.Cache) + { + settingPageItem.Instance = instance; + } + + return instance; + } + + return settingPageItem?.Instance; + } + + /// + /// Discovers and binds setting pages to an application. + /// + /// The context of the plugin whose setting pages are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds setting pages to an application. + /// + /// The context of the application whose pages are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers pages for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext.Assembly; + + foreach (var settingPageType in assembly.GetTypes() + .Where(x => x.IsClass && x.IsSealed && (x.GetInterfaces().Contains(typeof(ISettingPage))))) + { + var id = settingPageType.FullName?.ToLower(); + var context = default(string); + var group = default(string); + var section = SettingSection.Primary; + var hide = false; + var icon = default(string); + var cache = false; + + // determining attributes + foreach (var customAttribute in settingPageType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) + { + if (customAttribute.AttributeType == typeof(SettingContextAttribute)) + { + context = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(SettingGroupAttribute)) + { + group = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(SettingSectionAttribute)) + { + section = (SettingSection)Enum.Parse(typeof(SettingSection), customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); + } + else if (customAttribute.AttributeType == typeof(SettingHideAttribute)) + { + hide = true; + } + else if (customAttribute.AttributeType == typeof(IconAttribute)) + { + icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(CacheAttribute)) + { + cache = true; + } + } + // assign the fragment to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) + { + var settingPageContext = new SettingPageContext() + { + ApplicationContext = applicationContext, + PluginContext = pluginContext, + Context = context, + Section = section, + Group = group, + Hide = hide, + Icon = icon, + Cache = cache + }; + + // Create meta information of the setting page + var settingPageItem = new SettingPageItem() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + SettingPageContext = settingPageContext, + SettingPageClass = settingPageType, + Context = context, + Section = section, + Group = group, + Cache = cache + }; + + // Insert the settings page into the dictionary + if (_dictionary.AddSettingPageItem(settingPageItem)) + { + OnAddSettingPage(settingPageContext); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress:settingpagemanager.register", + id, + section, + applicationContext.ApplicationId + ) + ); + } + } + } + + // Logging + Log(); + } + + /// + /// Removes all elemets associated with the specified plugin context. + /// + /// The context of the plugin that contains the elemets to remove. + public void Remove(IPluginContext pluginContext) + { + _dictionary.Remove(pluginContext); + } + + /// + /// Removes all setting pages associated with the specified application context. + /// + /// The context of the application that contains the fragments to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + foreach (var pluginDict in _dictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var settingPageItem in appDict.Values + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i)) + { + OnRemoveSettingPage(settingPageItem.SettingPageContext); + settingPageItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + } + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// An enumeration of setting page contextes. + public IEnumerable GetSettingPages(Type settingPageType) + { + return _dictionary.Values + .SelectMany(a => a.Values) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .Where(x => x.SettingPageClass.Equals(settingPageType)) + .Select(x => x.SettingPageContext); + } + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// The context of the application. + /// An enumeration of setting page contextes. + public IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext) + { + return _dictionary.Values + .SelectMany(a => a) + .Where(a => a.Key.Equals(applicationContext)) + .Select(a => a.Value) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .Where(x => x.SettingPageClass.Equals(settingPageType)) + .Select(x => x.SettingPageContext); + } + + /// + /// Raises the AddSettingPage event. + /// + /// The setting page context. + private void OnAddSettingPage(ISettingPageContext settingPageContext) + { + AddSettingPage?.Invoke(this, settingPageContext); + } + + /// + /// Raises the RemoveSettingPage event. + /// + /// The setting page context. + private void OnRemoveSettingPage(ISettingPageContext settingPageContext) + { + RemoveSettingPage?.Invoke(this, settingPageContext); + } + + /// + /// Raises the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Raises the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + + /// + /// Raises the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + private void Log() + { + //output.Add + //( + // string.Empty.PadRight(deep) + + // I18N.Translate("webexpress.webui:settingpagemanager.titel") + //); + + //var log = new List + // { + // I18N.Translate("webexpress.webapp:pagesettingmanager.register"), + // " SettingContext = " + context ?? "null", + // " SettingSection = " + section.ToString(), + // " SettingGroup = " + group ?? "null", + // " SettingPage.Id = " + page?.Id ?? "null", + // " SettingPage.Hide = " + (page?.Hide != null ? page?.Hide.ToString() : "null") + // }; + + //_httpServerContext.Log.Debug(string.Join(Environment.NewLine, log)); + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageSearchResult.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageSearchResult.cs new file mode 100644 index 0000000..bbd473b --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageSearchResult.cs @@ -0,0 +1,30 @@ +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.SettingPage +{ + /// + /// Represents the result of a search on the settings page. + /// + public class SettingPageSearchResult + { + /// + /// Returns the setting context. + /// + public string Context { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Returns the group. + /// + public string Group { get; internal set; } + + /// + /// A list of all currently existing setting contexts that can be accessed through the settings page. + /// + public SettingPageItem Item { get; internal set; } + } +} diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index d4bba91..e58b62a 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -354,9 +354,9 @@ internal void Remove(IPluginContext pluginContext) } /// - /// Removes all jobs associated with the specified application context. + /// Removes all status pages associated with the specified application context. /// - /// The context of the application that contains the jobs to remove. + /// The context of the application that contains the status pages to remove. internal void Remove(IApplicationContext applicationContext) { if (applicationContext == null) @@ -398,7 +398,7 @@ private void OnRemoveStatusPage(IStatusPageContext statusPage) } /// - /// Handles the event when an plugin is added. + /// Raises the event when an plugin is added. /// /// The source of the event. /// The context of the plugin being added. @@ -408,7 +408,7 @@ private void OnAddPlugin(object sender, IPluginContext e) } /// - /// Handles the event when a plugin is removed. + /// Raises the event when a plugin is removed. /// /// The source of the event. /// The context of the plugin being removed. @@ -417,7 +417,7 @@ private void OnRemovePlugin(object sender, IPluginContext e) Remove(e); } /// - /// Handles the event when an application is removed. + /// Raises the event when an application is removed. /// /// The source of the event. /// The context of the application being removed. @@ -427,7 +427,7 @@ private void OnRemoveApplication(object sender, IApplicationContext e) } /// - /// Handles the event when an application is added. + /// Raises the event when an application is added. /// /// The source of the event. /// The context of the application being added. From e7f4e2fdc6dac91475e09db4fbd2f397cf99f5ae Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 2 Nov 2024 12:38:29 +0100 Subject: [PATCH 038/162] add asset manager - implemented asset manager - resolved multiple bugs to enhance resource handling and stability --- .../Assets/css/mycss.css | 14 + .../Assets/js/myjavascript.js | 9 + .../Assets/js/myjavascript.mini.js | 1 + .../Fixture/UnitTestControlFixture.cs | 23 +- .../Manager/UnitTestAssetManager.cs | 169 +++++++ .../Manager/UnitTestSitemapManager.cs | 9 +- .../WebExpress.WebCore.Test.csproj | 6 + src/WebExpress.WebCore/WebAsset/Asset.cs | 164 +++++++ .../WebAsset/AssetContext.cs | 84 ++++ .../WebAsset/AssetManager.cs | 420 ++++++++++++++++++ src/WebExpress.WebCore/WebAsset/IAsset.cs | 18 + .../WebAsset/IAssetContext.cs | 12 + .../WebAsset/IAssetManager.cs | 43 ++ .../WebAsset/Model/AssetEndpointDictionary.cs | 15 + .../WebAsset/Model/AssetItem.cs | 53 +++ .../WebAsset/Model/AssetItemDictionary.cs | 72 +++ .../WebComponent/ComponentHub.cs | 10 + .../WebComponent/IComponentHub.cs | 7 + .../WebEndpoint/EndpointManager.cs | 5 + .../WebEvent/EventManager.cs | 2 +- .../WebPage/Model/PageDictionary.cs | 18 +- .../WebResource/Model/ResourceDictionary.cs | 22 +- .../WebResource/Model/ResourceItem.cs | 8 +- .../WebResource/ResourceManager.cs | 5 +- .../WebRestAPI/Model/RestApiDictionary.cs | 24 +- .../WebRestAPI/Model/RestApiItem.cs | 6 - .../WebRestAPI/RestApiManager.cs | 5 +- 27 files changed, 1143 insertions(+), 81 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Assets/css/mycss.css create mode 100644 src/WebExpress.WebCore.Test/Assets/js/myjavascript.js create mode 100644 src/WebExpress.WebCore.Test/Assets/js/myjavascript.mini.js create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs create mode 100644 src/WebExpress.WebCore/WebAsset/Asset.cs create mode 100644 src/WebExpress.WebCore/WebAsset/AssetContext.cs create mode 100644 src/WebExpress.WebCore/WebAsset/AssetManager.cs create mode 100644 src/WebExpress.WebCore/WebAsset/IAsset.cs create mode 100644 src/WebExpress.WebCore/WebAsset/IAssetContext.cs create mode 100644 src/WebExpress.WebCore/WebAsset/IAssetManager.cs create mode 100644 src/WebExpress.WebCore/WebAsset/Model/AssetEndpointDictionary.cs create mode 100644 src/WebExpress.WebCore/WebAsset/Model/AssetItem.cs create mode 100644 src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs diff --git a/src/WebExpress.WebCore.Test/Assets/css/mycss.css b/src/WebExpress.WebCore.Test/Assets/css/mycss.css new file mode 100644 index 0000000..d0446e5 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Assets/css/mycss.css @@ -0,0 +1,14 @@ +/* This CSS file styles the "Hello World" text */ + +/* Style for the body */ +body { + background-color: #f0f0f0; /* Light grey background for the entire page */ + font-family: Arial, sans-serif; /* Set font to Arial */ +} + +/* Style for the heading */ +h1 { + color: #333; /* Dark grey color for the text */ + text-align: center; /* Center the text */ + margin-top: 20%; /* Add space at the top */ +} diff --git a/src/WebExpress.WebCore.Test/Assets/js/myjavascript.js b/src/WebExpress.WebCore.Test/Assets/js/myjavascript.js new file mode 100644 index 0000000..2f61063 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Assets/js/myjavascript.js @@ -0,0 +1,9 @@ +// This script prints "Hello World" to the browser console + +// Define a function that prints "Hello World" +function sayHello() { + console.log("Hello World"); // This prints "Hello World" to the console +} + +// Call the function to print "Hello World" +sayHello(); \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Assets/js/myjavascript.mini.js b/src/WebExpress.WebCore.Test/Assets/js/myjavascript.mini.js new file mode 100644 index 0000000..ec1bbc5 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Assets/js/myjavascript.mini.js @@ -0,0 +1 @@ +function sayHello(){console.log("Hello World");}sayHello(); \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 32cc6cc..90e3cec 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -11,6 +11,7 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Fixture { @@ -97,13 +98,21 @@ public static ComponentHub CreateAndRegisterComponentHubMock() /// /// Create a fake request. /// - /// The content. + /// The content of the request. + /// The URI of the request. /// A fake request for testing. - public static WebMessage.Request CrerateRequestMock(string content = "") + public static WebMessage.Request CrerateRequestMock(string content = "", string uri = "") { var context = CreateHttpContextMock(content); - return context.Request; + var request = context.Request; + + if (!string.IsNullOrEmpty(uri)) + { + request.Uri = new UriResource(uri); + } + + return request; } /// @@ -216,10 +225,12 @@ public static string GetEmbeddedResource(string fileName) var resourceName = assembly.GetManifestResourceNames() .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); - using Stream stream = assembly.GetManifestResourceStream(resourceName); - using StreamReader reader = new(stream); + using var stream = assembly.GetManifestResourceStream(resourceName); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + var data = memoryStream.ToArray(); - return reader.ReadToEnd(); + return Encoding.UTF8.GetString(data); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs new file mode 100644 index 0000000..b95b4a8 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs @@ -0,0 +1,169 @@ +using System.Text; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebAsset; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebSitemap; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the asset manager. + /// + [Collection("NonParallelTests")] + public class UnitTestAssetManager + { + /// + /// Test the register function of the asset manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(9, componentHub.AssetManager.Assets.Count()); + } + + /// + /// Test the remove function of the asset manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var resourceManager = componentHub.AssetManager as AssetManager; + + // test execution + resourceManager.Remove(plugin); + + Assert.Empty(componentHub.AssetManager.Assets); + } + + /// + /// Test the id property of the asset. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "css.mycss.css")] + [InlineData(typeof(TestApplicationA), "js.myjavascript.js")] + [InlineData(typeof(TestApplicationA), "js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationB), "css.mycss.css")] + [InlineData(typeof(TestApplicationB), "js.myjavascript.js")] + [InlineData(typeof(TestApplicationB), "js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationC), "css.mycss.css")] + [InlineData(typeof(TestApplicationC), "js.myjavascript.js")] + [InlineData(typeof(TestApplicationC), "js.myjavascript.mini.js")] + public void Id(Type applicationType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == id); + + // test execution + Assert.Equal(id, asset?.EndpointId); + } + + /// + /// Test the uri property of the asset. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "/appa/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationA), "/appa/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationA), "/appa/assets/js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationB), "/appb/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationB), "/appb/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationB), "/appb/assets/js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationC), "/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationC), "/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationC), "/assets/js.myjavascript.mini.js")] + public void Uri(Type applicationType, string uri) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == Path.GetFileName(uri)); + + // test execution + Assert.Equal(uri, asset?.Uri); + } + + /// + /// Test the request of the asset. + /// + [Theory] + [InlineData("http://localhost:8080/appa/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/appa/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/appa/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/appa/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/appb/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/appb/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/appb/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/appb/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/appb/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/appb/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + public void Request(string uri, string id, string resource) + { + // preconditions + var embeddedResource = UnitTestControlFixture.GetEmbeddedResource(resource); + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestControlFixture.CreateHttpContextMock(); + var httpServerContext = UnitTestControlFixture.CreateHttpServerContextMock(); + componentHub.SitemapManager.Refresh(); + + // test execution + var searchResult = componentHub.SitemapManager.SearchResource(new System.Uri(uri), new SearchContext() + { + HttpServerContext = httpServerContext, + Culture = httpServerContext.Culture, + HttpContext = context + }); + + var response = componentHub.EndpointManager.HandleRequest(UnitTestControlFixture.CrerateRequestMock("", uri), searchResult.EndpointContext); + + Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); + Assert.IsNotType(response); + Assert.Equal(embeddedResource, Encoding.UTF8.GetString(response.Content as byte[])); + } + + /// + /// Tests whether the asset manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.AssetManager.GetType())); + } + + /// + /// Tests whether the asset context implements interface IContext. + /// + [Fact] + public void IsIContext() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + foreach (var asset in componentHub.AssetManager.Assets) + { + Assert.True(typeof(IContext).IsAssignableFrom(asset.GetType()), $"Asset context {asset.GetType().Name} does not implement IContext."); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index d35634b..fd8d84a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,7 +22,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(42, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(45, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -53,6 +53,12 @@ public void Refresh() [InlineData("http://localhost:8080/appa/1/apia", "webexpress.webcore.test.testrestapia")] [InlineData("http://localhost:8080/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] [InlineData("http://localhost:8080/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] + [InlineData("http://localhost:8080/appa/assets/css/mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/appa/assets/css.mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) @@ -87,7 +93,6 @@ public void SearchResource(string uri, string id) [InlineData(typeof(TestPageA), "/appa/pagea")] [InlineData(typeof(TestPageB), "/appa/resa/pageb")] [InlineData(typeof(TestPageC), "/appa/pagec")] - public void GetUri(Type resourceType, string expected) { // preconditions diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index 0b4724d..9cc3f69 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -10,6 +10,9 @@ + + + @@ -29,6 +32,9 @@ + + + diff --git a/src/WebExpress.WebCore/WebAsset/Asset.cs b/src/WebExpress.WebCore/WebAsset/Asset.cs new file mode 100644 index 0000000..2102a96 --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/Asset.cs @@ -0,0 +1,164 @@ +using System; +using System.IO; +using System.Reflection; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebAsset; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebResource +{ + /// + /// Delivery of a resource embedded in the assembly. + /// + public class Asset : IAsset + { + private readonly IComponentHub _componentHub; + private readonly IAssetContext _assetContext; + private readonly IHttpServerContext _httpServerContext; + private readonly string _embeddedResource; + private byte[] _data; + + /// + /// Returns the root directory. + /// + public string AssetDirectory { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The asset context. + /// The server context. + /// The embedded resource name. + public Asset(IComponentHub componentHub, IAssetContext assetContext, IHttpServerContext httpServerContext, string embeddedResource) + { + _componentHub = componentHub; + _assetContext = assetContext; + _httpServerContext = httpServerContext; + _embeddedResource = embeddedResource; + + var assembly = _assetContext.PluginContext.Assembly; + _data = GetData(assembly); + } + + /// + /// Processing of the resource. + /// + /// The request. + /// The response. + public Response Process(Request request) + { + if (_data == null) + { + return new ResponseNotFound(); + } + + var extension = Path.GetExtension(_assetContext.EndpointId)?.ToLower() ?? ""; + var response = new ResponseOK(); + + response.Header.CacheControl = "public, max-age=31536000"; + response.Header.ContentLength = _data.Length; + response.Content = _data; + + switch (extension) + { + case ".pdf": + response.Header.ContentType = "application/pdf"; + break; + case ".txt": + response.Header.ContentType = "text/plain"; + break; + case ".css": + response.Header.ContentType = "text/css"; + break; + case ".js": + response.Header.ContentType = "application/javascript"; + break; + case ".xml": + response.Header.ContentType = "text/xml"; + break; + case ".html": + case ".htm": + response.Header.ContentType = "text/html"; + break; + case ".zip": + response.Header.ContentDisposition = "attatchment; filename=" + _assetContext.EndpointId + "; size=" + _data.LongLength; + response.Header.ContentType = "application/zip"; + break; + case ".doc": + case ".docx": + response.Header.ContentType = "application/msword"; + break; + case ".xls": + case ".xlx": + response.Header.ContentType = "application/vnd.ms-excel"; + break; + case ".ppt": + response.Header.ContentType = "application/vnd.ms-powerpoint"; + break; + case ".gif": + response.Header.ContentType = "image/gif"; + break; + case ".png": + response.Header.ContentType = "image/png"; + break; + case ".svg": + response.Header.ContentType = "image/svg+xml"; + break; + case ".jpeg": + case ".jpg": + response.Header.ContentType = "image/jpg"; + break; + case ".ico": + response.Header.ContentType = "image/x-icon"; + break; + case ".mp3": + response.Header.ContentType = "audio/mpeg"; + break; + case ".mp4": + response.Header.ContentType = "video/mp4"; + break; + default: + return new ResponseNotFound(); + } + + _httpServerContext.Log.Debug(I18N.Translate + ( + "webexpress:asset.file", + request.RemoteEndPoint, request.Uri + )); + + return response; + } + + /// + /// Reads the data of a specified resource. + /// + /// The assembly. + /// The data. + private byte[] GetData(Assembly assembly) + { + if (assembly == null || _embeddedResource == null) + { + return []; + } + + using var stream = assembly.GetManifestResourceStream(_embeddedResource); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + + return memoryStream.ToArray(); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + _data = null; + + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs new file mode 100644 index 0000000..5caca6c --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -0,0 +1,84 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebAsset +{ + /// + /// Represents the context of a asset. + /// + public class AssetContext : IAssetContext + { + private readonly UriResource _contextPath; + private readonly IUriPathSegment _pathSegment; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the conditions that must be met for the resource to be active. + /// + public IEnumerable Conditions => []; + + /// + /// Returns the resource id. + /// + public string EndpointId { get; internal set; } + + /// + /// Returns the parent or null if not used. + /// + public IEndpointContext ParentContext => null; + + /// + /// Returns whether the resource is created once and reused each time it is called. + /// + public bool Cache => true; + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; internal set; } + + /// + /// Returns the context path. + /// + public UriResource ContextPath => UriResource.Combine(ApplicationContext.ContextPath, _contextPath); + + /// + /// Returns the uri. + /// + public UriResource Uri => ContextPath.Append(_pathSegment); + + /// + /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. + /// + /// The endpoint manager responsible for managing endpoints. + /// The context path of the resource. + /// The path segment of the resource. + public AssetContext(UriResource contextPath, IUriPathSegment pathSegment) + { + _contextPath = contextPath; + _pathSegment = pathSegment; + } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Asset: {EndpointId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs new file mode 100644 index 0000000..40f9b8a --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAsset.Model; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebAsset +{ + /// + /// The asset manager manages WebExpress elements, which can be called with a URI (Uniform Resource Identifier). + /// + public sealed class AssetManager : IAssetManager, ISystemComponent + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly AssetItemDictionary _itemDictionary = []; + private readonly AssetEndpointDictionary _endpointDictionary = []; + + /// + /// An event that fires when an asset is added. + /// + public event EventHandler AddResource; + + /// + /// An event that fires when an asset is removed. + /// + public event EventHandler RemoveResource; + + /// + /// Returns all asset contexts. + /// + public IEnumerable Assets => _itemDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.AssetContext); + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The reference to the context of the host. + private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; + + var endpointtRegistration = new EndpointRegistration() + { + EndpointResolver = (type, applicationContext) => _endpointDictionary + .Where(x => x.Key == applicationContext) + .Select(x => x.Value) + .Where(x => x.Item2.GetType() == type) + .Select(x => x.Item1), + EndpointsResolver = () => _endpointDictionary + .Select(x => x.Value) + .Select(x => x.Item1), + HandleRequest = (request, endpointContext) => + { + var assetContext = endpointContext as IAssetContext; + var asset = _itemDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .FirstOrDefault(x => request.Uri.ToString().ToLower().Replace('/', '.').EndsWith(x.AssetContext.EndpointId)); + + if (asset != null) + { + return asset.Instance.Process(request); + } + + return new ResponseNotFound(); + } + }; + + AddResource += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemoveResource += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + + _componentHub.EndpointManager.Register(endpointtRegistration); + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress:assetmanager.initialization") + ); + } + + /// + /// Discovers and binds resources to an application. + /// + /// The context of the plugin whose resources are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_itemDictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds resources to an application. + /// + /// The context of the application whose resources are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_itemDictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; + var assemblName = assembly.GetName().Name; + var embeddedResources = assembly.GetManifestResourceNames(); + + foreach (var resource in embeddedResources) + { + if (resource.StartsWith(assemblName + ".Assets.", StringComparison.OrdinalIgnoreCase)) + { + var id = resource[(assemblName.Length + 8)..]; + + // assign the asset to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) + { + var assetContext = new AssetContext(new UriResource(), new UriPathSegmentConstant($"assets/{id}")) + { + EndpointId = id, + PluginContext = pluginContext, + ApplicationContext = applicationContext, + IncludeSubPaths = false + }; + + var assetItem = new AssetItem(_componentHub.AssetManager) + { + AssetClass = typeof(Asset), + AssetContext = assetContext, + Instance = ComponentActivator.CreateInstance(typeof(Asset), assetContext, _httpServerContext, _componentHub, resource) + }; + + if (_itemDictionary.AddAssetItem(pluginContext, applicationContext, assetItem)) + { + OnAddResource(assetContext); + _httpServerContext?.Log.Debug( + I18N.Translate( + "webexpress:assetmanager.addresource", + id, + applicationContext.ApplicationId + ) + ); + } + } + } + } + } + + /// + /// Removes all resources associated with the specified plugin context. + /// + /// The context of the plugin that contains the resources to remove. + internal void Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return; + } + + // the plugin has not been registered in the manager + if (_itemDictionary.TryGetValue(pluginContext, out var value)) + { + foreach (var resourceItem in value.Values + .SelectMany(x => x)) + { + OnRemoveResource(resourceItem.AssetContext); + resourceItem.Dispose(); + } + + _itemDictionary.Remove(pluginContext); + } + } + + /// + /// Removes all assets associated with the specified application context. + /// + /// The context of the application that contains the resources to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + foreach (var pluginDict in _itemDictionary.Values) + { + foreach (var assetList in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var assetItem in assetList) + { + OnRemoveResource(assetItem.AssetContext); + assetItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + } + + /// + /// Returns an enumeration of all containing asset items of a plugin. + /// + /// A context of a plugin whose resources are to be registered. + /// An enumeration of resource items. + private IEnumerable GetAssetItems(IPluginContext pluginContext) + { + if (_itemDictionary.TryGetValue(pluginContext, out var pluginResources)) + { + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x); + } + + return []; + } + + /// + /// Returns an enumeration of all containing asset contexts of a plugin. + /// + /// A context of a plugin whose asset are to be registered. + /// An enumeration of asset contexts. + public IEnumerable GetAssets(IPluginContext pluginContext) + { + if (_itemDictionary.TryGetValue(pluginContext, out var pluginResources)) + { + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.AssetContext); + } + + return []; + } + + /// + /// Returns an enumeration of asset contextes. + /// + /// The context of the application. + /// An enumeration of asset contextes. + public IEnumerable GetAssets(IApplicationContext applicationContext) + { + return _itemDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Where(x => x.AssetContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.AssetContext); + } + + /// + /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. + /// + /// The context used for asset creation. + /// The culture with the language settings. + /// The created or cached resource. + private IAsset CreateAssetInstance(IAssetContext assetContext, CultureInfo culture) + { + var resourceItem = _itemDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .FirstOrDefault(x => x.AssetContext.Equals(assetContext)); + + if (resourceItem != null && resourceItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance(resourceItem.AssetClass, assetContext, _httpServerContext, _componentHub); + + if (instance is II18N i18n) + { + i18n.Culture = culture; + } + + resourceItem.Instance = instance; + + return instance; + } + + return resourceItem?.Instance as IAsset; + } + + /// + /// Raises the AddResource event. + /// + /// The asset context. + private void OnAddResource(IAssetContext resourceContext) + { + AddResource?.Invoke(this, resourceContext); + } + + /// + /// Raises the RemoveResource event. + /// + /// The asset context. + private void OnRemoveResource(IAssetContext resourceContext) + { + RemoveResource?.Invoke(this, resourceContext); + } + + /// + /// Raises the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Raises the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + + var assembly = typeof(AssetManager).Assembly; + var assemblyName = assembly.GetName().Name.ToLower(); + + var context = new AssetContext(new UriResource(), new UriPathSegmentConstant("assets")) + { + ApplicationContext = e, + PluginContext = new PluginContext() + { + PluginId = assemblyName, + Assembly = assembly + }, + EndpointId = assemblyName + ".asset", + IncludeSubPaths = true + }; + + var asset = ComponentActivator.CreateInstance(typeof(Asset), context, _httpServerContext, _componentHub); + + _endpointDictionary.TryAdd(e, (context, asset)); + } + + /// + /// Raises the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + + _endpointDictionary.Remove(e); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + private void Log() + { + //foreach (var resourcenItem in GetResorceItems(pluginContext)) + //{ + // output.Add + // ( + // string.Empty.PadRight(deep) + + // I18N.Translate + // ( + // "webexpress:resourcemanager.resource", + // resourcenItem?.ResourceContext?.EndpointId, + // string.Join(",", resourcenItem.ResourceContext?.ApplicationContext?.ApplicationId) + // ) + // ); + //} + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + } + } +} diff --git a/src/WebExpress.WebCore/WebAsset/IAsset.cs b/src/WebExpress.WebCore/WebAsset/IAsset.cs new file mode 100644 index 0000000..78d8e5c --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/IAsset.cs @@ -0,0 +1,18 @@ +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebAsset +{ + /// + /// Defines the contract for a asset component. + /// + public interface IAsset : IEndpoint + { + /// + /// Processing of the resource. + /// + /// The request. + /// The response. + Response Process(Request request); + } +} diff --git a/src/WebExpress.WebCore/WebAsset/IAssetContext.cs b/src/WebExpress.WebCore/WebAsset/IAssetContext.cs new file mode 100644 index 0000000..93ec6a8 --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/IAssetContext.cs @@ -0,0 +1,12 @@ +using WebExpress.WebCore.WebEndpoint; + +namespace WebExpress.WebCore.WebAsset +{ + /// + /// Defines the context for a asset, providing access to various related contexts and properties. + /// + public interface IAssetContext : IEndpointContext + { + + } +} diff --git a/src/WebExpress.WebCore/WebAsset/IAssetManager.cs b/src/WebExpress.WebCore/WebAsset/IAssetManager.cs new file mode 100644 index 0000000..3848b20 --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/IAssetManager.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebAsset +{ + /// + /// The asset manager manages resources elements, which can be called with a URI (Uniform Resource Identifier). + /// + public interface IAssetManager : IComponentManager + { + /// + /// An event that fires when an asset is added. + /// + event EventHandler AddResource; + + /// + /// An event that fires when an asset is removed. + /// + event EventHandler RemoveResource; + + /// + /// Returns all asset contexts. + /// + IEnumerable Assets { get; } + + /// + /// Returns an enumeration of all containing asset contexts of a plugin. + /// + /// A context of a plugin whose resources are to be registered. + /// An enumeration of resource contexts. + IEnumerable GetAssets(IPluginContext pluginContext); + + /// + /// Returns an enumeration of asset contextes. + /// + /// The context of the application. + /// An enumeration of asset contextes. + IEnumerable GetAssets(IApplicationContext applicationContext); + } +} diff --git a/src/WebExpress.WebCore/WebAsset/Model/AssetEndpointDictionary.cs b/src/WebExpress.WebCore/WebAsset/Model/AssetEndpointDictionary.cs new file mode 100644 index 0000000..5e9f39c --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/Model/AssetEndpointDictionary.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; + +namespace WebExpress.WebCore.WebAsset.Model +{ + /// + /// Represents a dictionary that maps plugin contexts to application contexts and assets. + /// key = plugin context + /// value = { key = application context, value = asset } + /// + internal class AssetEndpointDictionary : Dictionary + { + + } +} diff --git a/src/WebExpress.WebCore/WebAsset/Model/AssetItem.cs b/src/WebExpress.WebCore/WebAsset/Model/AssetItem.cs new file mode 100644 index 0000000..b0a04dd --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/Model/AssetItem.cs @@ -0,0 +1,53 @@ +using System; + +namespace WebExpress.WebCore.WebAsset.Model +{ + /// + /// A assat element that contains meta information about a asset. + /// + internal class AssetItem : IDisposable + { + private readonly IAssetManager _assetManager; + + /// + /// Returns or sets the type of asset. + /// + public Type AssetClass { get; set; } + + /// + /// Returns or sets the instance of the asset, if the asset is cached, otherwise null. + /// + public IAsset Instance { get; set; } + + /// + /// Returns the asset context. + /// + public IAssetContext AssetContext { get; internal set; } + + /// + /// Initializes a new instance of the class. + /// + /// The asset manager. + internal AssetItem(IAssetManager assetManager) + { + _assetManager = assetManager; + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + + } + + /// + /// Convert the asset element to a string. + /// + /// The asset element in its string representation. + public override string ToString() + { + return $"Asset: '{AssetContext?.EndpointId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs b/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs new file mode 100644 index 0000000..3fcdea5 --- /dev/null +++ b/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebAsset.Model +{ + /// + /// Represents a dictionary that maps plugin contexts to application contexts and asset items. + /// key = plugin context + /// value = { key = resource type, value = ressource item } + /// + internal class AssetItemDictionary : Dictionary>> + { + /// + /// Adds a asset item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The resource item. + /// True if the resource item was added successfully, false if an element with the same status code already exists. + public bool AddAssetItem(IPluginContext pluginContext, IApplicationContext applicationContext, AssetItem assetItem) + { + var type = assetItem.AssetClass; + + if (!typeof(IAsset).IsAssignableFrom(type)) + { + return false; + } + + if (!ContainsKey(pluginContext)) + { + this[pluginContext] = []; + } + + var appContextDict = this[pluginContext]; + + if (!appContextDict.ContainsKey(applicationContext)) + { + appContextDict[applicationContext] = []; + } + + var assetList = appContextDict[applicationContext]; + + assetList.RemoveAll(x => x.AssetContext?.EndpointId == assetItem.AssetContext.EndpointId); + assetList.Add(assetItem); + + return true; + } + + /// + /// Returns the asset items from the dictionary. + /// + /// The application context. + /// An IEnumerable of asset items + public IEnumerable GetAssetItems(IApplicationContext applicationContext) + { + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var assetList = appContextDict[applicationContext]; + + return assetList; + } + } + + return []; + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 1b44825..20f59c6 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -4,6 +4,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.SettingPage; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebComponent.Model; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; @@ -34,6 +35,7 @@ public class ComponentHub : IComponentHub private readonly PluginManager _pluginManager; private readonly ApplicationManager _applicationManager; private readonly EndpointManager _endpointManager; + private readonly AssetManager _assetManager; private readonly ResourceManager _resourceManager; private readonly PageManager _pageManager; private readonly SettingPageManager _settingPageManager; @@ -66,6 +68,7 @@ public class ComponentHub : IComponentHub _applicationManager, _endpointManager, _sitemapManager, + _assetManager, _resourceManager, _pageManager, _settingPageManager, @@ -126,6 +129,12 @@ public class ComponentHub : IComponentHub /// The instance of the endpoint manager. public IEndpointManager EndpointManager => _endpointManager; + /// + /// Returns the asset manager. + /// + /// The instance of the asset manager. + public IAssetManager AssetManager => _assetManager; + /// /// Returns the resource manager. /// @@ -191,6 +200,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; _endpointManager = CreateInstance(typeof(EndpointManager)) as EndpointManager; + _assetManager = CreateInstance(typeof(AssetManager)) as AssetManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; _pageManager = CreateInstance(typeof(PageManager)) as PageManager; _settingPageManager = CreateInstance(typeof(SettingPageManager)) as SettingPageManager; diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index d74b1af..3f7999c 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -3,6 +3,7 @@ using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.SettingPage; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; using WebExpress.WebCore.WebJob; @@ -87,6 +88,12 @@ public interface IComponentHub : IComponentManager /// The instance of the endpoint manager. IEndpointManager EndpointManager { get; } + /// + /// Returns the asset manager. + /// + /// The instance of the asset manager. + public IAssetManager AssetManager { get; } + /// /// Returns the resource manager. /// diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 5adfbc7..799cd54 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -87,6 +87,11 @@ public void Remove() where T : IEndpointContext /// An enumeration of endpoint contexts. public IEnumerable GetEndpoints(Type endpointType, IApplicationContext applicationContext = null) { + if (endpointType == null) + { + return []; + } + return _registrations.SelectMany(x => x.Value.EndpointResolver(endpointType, applicationContext)); } diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index e664630..d2772de 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -181,7 +181,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); + appContextDict[applicationContext] = []; } var pageDict = appContextDict[applicationContext]; @@ -108,7 +107,7 @@ public IEnumerable GetPageItems(IApplicationContext applicationContext { if (!typeof(IPage).IsAssignableFrom(pageType)) { - return Enumerable.Empty(); + return []; } if (ContainsKey(applicationContext?.PluginContext)) @@ -128,18 +127,5 @@ public IEnumerable GetPageItems(IApplicationContext applicationContext return []; } - - /// - /// Returns all page contexts for a given plugin context. - /// - /// The plugin context. - /// An IEnumerable of page contexts. - public IEnumerable GetPages(IPluginContext pluginContext) - { - return this.Where(entry => entry.Key == pluginContext) - .SelectMany(entry => entry.Value.Values) - .SelectMany(dict => dict.Values) - .Select(x => x.ContextPath as IPageContext); - } } } diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs index 6c1dbd4..8df59ca 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; @@ -31,14 +30,14 @@ public bool AddResourceItem(IPluginContext pluginContext, IApplicationContext ap if (!ContainsKey(pluginContext)) { - this[pluginContext] = new Dictionary>(); + this[pluginContext] = []; } var appContextDict = this[pluginContext]; if (!appContextDict.ContainsKey(applicationContext)) { - appContextDict[applicationContext] = new Dictionary(); + appContextDict[applicationContext] = []; } var resourceDict = appContextDict[applicationContext]; @@ -108,7 +107,7 @@ public IEnumerable GetResourceItems(IApplicationContext applicatio { if (!typeof(IResource).IsAssignableFrom(resourceType)) { - return Enumerable.Empty(); + return []; } if (ContainsKey(applicationContext?.PluginContext)) @@ -121,25 +120,12 @@ public IEnumerable GetResourceItems(IApplicationContext applicatio if (resourceDict.ContainsKey(resourceType)) { - return new List { resourceDict[resourceType] }; + return [resourceDict[resourceType]]; } } } return []; } - - /// - /// Returns all resource contexts for a given plugin context. - /// - /// The plugin context. - /// An IEnumerable of resource contexts. - public IEnumerable GetResources(IPluginContext pluginContext) - { - return this.Where(entry => entry.Key == pluginContext) - .SelectMany(entry => entry.Value.Values) - .SelectMany(dict => dict.Values) - .Select(x => x.ContextPath as IResourceContext); - } } } diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index 7b43008..d877efa 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; -using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource.Model @@ -61,11 +60,6 @@ internal class ResourceItem : IDisposable /// public bool Cache { get; set; } - /// - /// Returns the log to write status messages to the console and to a log file. - /// - public ILog Log { get; internal set; } - /// /// Returns the resource context. /// @@ -94,7 +88,7 @@ public void Dispose() /// The resource element in its string representation. public override string ToString() { - return $"Resource '{ResourceContext?.EndpointId}'"; + return $"Resource: '{ResourceContext?.EndpointId}'"; } } } diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 783de30..6d2a7fe 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -165,7 +165,7 @@ private void Register(IPluginContext pluginContext, IEnumerable>(); + this[pluginContext] = []; } var appContextDict = this[pluginContext]; if (!appContextDict.ContainsKey(applicationContext)) { - appContextDict[applicationContext] = new Dictionary(); + appContextDict[applicationContext] = []; } var restApiDict = appContextDict[applicationContext]; @@ -108,7 +107,7 @@ public IEnumerable GetRestApiItems(IApplicationContext applicationC { if (!typeof(IRestApi).IsAssignableFrom(restApiType)) { - return Enumerable.Empty(); + return []; } if (ContainsKey(applicationContext?.PluginContext)) @@ -121,25 +120,12 @@ public IEnumerable GetRestApiItems(IApplicationContext applicationC if (restApiDict.ContainsKey(restApiType)) { - return new List { restApiDict[restApiType] }; + return [restApiDict[restApiType]]; } } } - return Enumerable.Empty(); - } - - /// - /// Returns all rest api contexts for a given plugin context. - /// - /// The plugin context. - /// An IEnumerable of rest api contexts. - public IEnumerable GetRestApis(IPluginContext pluginContext) - { - return this.Where(entry => entry.Key == pluginContext) - .SelectMany(entry => entry.Value.Values) - .SelectMany(dict => dict.Values) - .Select(x => x.ContextPath as IRestApiContext); + return []; } } } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs index fa6f5ad..b51acae 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi.Model @@ -68,11 +67,6 @@ internal class RestApiItem : IDisposable /// public bool Optional { get; set; } - /// - /// Returns the log to write status messages to the console and to a log file. - /// - public ILog Log { get; internal set; } - /// /// Returns the rest api contexts. /// diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 055a0d2..67fc556 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -361,7 +361,7 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Sun, 3 Nov 2024 11:22:48 +0100 Subject: [PATCH 039/162] error fixes and code optimizations --- .../Manager/UnitTestSettingPageManager.cs | 6 +- .../TestSettingPageA.cs | 70 +++++++++++++++++++ .../TestSettingPageB.cs | 70 +++++++++++++++++++ .../WebAsset/AssetManager.cs | 2 +- .../WebAsset/Model/AssetItemDictionary.cs | 11 +-- .../WebComponent/ComponentHub.cs | 2 +- .../WebComponent/IComponentHub.cs | 2 +- .../WebEvent/EventManager.cs | 2 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 2 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 2 +- .../WebResource/ResourceManager.cs | 2 +- .../WebRestAPI/RestApiManager.cs | 2 +- .../WebSettingPage/ISettingPage.cs | 2 +- .../WebSettingPage/ISettingPageContext.cs | 2 +- .../WebSettingPage/ISettingPageManager.cs | 2 +- .../Model/SettingPageDictionary.cs | 6 +- .../Model/SettingPageDictionaryItemContext.cs | 29 ++++---- .../Model/SettingPageDictionaryItemGroup.cs | 41 +++++++---- .../Model/SettingPageDictionaryItemSection.cs | 28 ++++---- .../WebSettingPage/Model/SettingPageItem.cs | 2 +- .../WebSettingPage/SettingPageContext.cs | 2 +- .../WebSettingPage/SettingPageManager.cs | 7 +- .../WebSettingPage/SettingPageSearchResult.cs | 2 +- .../WebStatusPage/StatusPageManager.cs | 2 +- 24 files changed, 226 insertions(+), 72 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestSettingPageA.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingPageB.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 0963405..95677ed 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -1,6 +1,6 @@ -using WebExpress.WebCore.SettingPage; -using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebSettingPage; namespace WebExpress.WebCore.Test.Manager { @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(0, componentHub.SettingPageManager.SettingPages.Count()); + Assert.Equal(6, componentHub.SettingPageManager.SettingPages.Count()); } /// diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/TestSettingPageA.cs new file mode 100644 index 0000000..4d9a893 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingPageA.cs @@ -0,0 +1,70 @@ +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebSettingPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:settingpagea.label")] + [Segment("settingpagea", "webindex:homepage.label")] + [ContextPath(null)] + public sealed class TestSettingPageA : ISettingPage + { + /// + /// Returns or sets the title of the setting page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the setting page context. + /// + public ISettingPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the setting page. + public TestSettingPageA(ISettingPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + + /// + /// Processing of the page. + /// + /// The context for rendering the setting page. + public void Process(IRenderContext context) + { + // test the context + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/TestSettingPageB.cs new file mode 100644 index 0000000..bf8f202 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingPageB.cs @@ -0,0 +1,70 @@ +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebSettingPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:settingpageb.label")] + [Segment("settingpagea", "webindex:homepage.label")] + [ContextPath(null)] + public sealed class TestSettingPageB : ISettingPage + { + /// + /// Returns or sets the title of the setting page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the setting page context. + /// + public ISettingPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the setting page. + public TestSettingPageB(ISettingPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Redirects to the specified URI. + /// + /// The URI to redirect to. + public void Redirecting(string uri) + { + + } + + /// + /// Processing of the page. + /// + /// The context for rendering the setting page. + public void Process(IRenderContext context) + { + // test the context + if (context == null) + { + throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 40f9b8a..6a715e4 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -145,7 +145,7 @@ private void Register(IPluginContext pluginContext, IEnumerable value)) { - appContextDict[applicationContext] = []; + value = ([]); + appContextDict[applicationContext] = value; } - var assetList = appContextDict[applicationContext]; + var assetList = value; assetList.RemoveAll(x => x.AssetContext?.EndpointId == assetItem.AssetContext.EndpointId); assetList.Add(assetItem); @@ -58,9 +59,9 @@ public IEnumerable GetAssetItems(IApplicationContext applicationConte { var appContextDict = this[applicationContext?.PluginContext]; - if (appContextDict.ContainsKey(applicationContext)) + if (appContextDict.TryGetValue(applicationContext, out List value)) { - var assetList = appContextDict[applicationContext]; + var assetList = value; return assetList; } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 20f59c6..727189a 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebComponent.Model; diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 3f7999c..bc39f80 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebEndpoint; diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index d2772de..954183e 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -182,7 +182,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// The type of the render context. - public interface ISettingPage : IPage where T : IRenderContext + public interface ISettingPage : ISettingPage where T : IRenderContext { } } diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index 128463f..d79c205 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebSettingPage.Model; -namespace WebExpress.WebCore.SettingPage +namespace WebExpress.WebCore.WebSettingPage { /// /// Interface representing the context of a setting page. diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs index 421549b..5f7dc3e 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebComponent; -namespace WebExpress.WebCore.SettingPage +namespace WebExpress.WebCore.WebSettingPage { /// /// Interface for managing setting pages. diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs index d86a97d..2dfd507 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.SettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; @@ -26,6 +25,7 @@ internal class SettingPageDictionary : Dictionary /// The settings page to insert. + /// True if the settings page was added successfully; otherwise, false. public bool AddSettingPageItem(SettingPageItem item) { if (!TryGetValue(item.PluginContext, out var appDict)) @@ -40,9 +40,7 @@ public bool AddSettingPageItem(SettingPageItem item) appDict.Add(item.ApplicationContext, contextDict); } - contextDict.AddPage(item.Context, item.Section, item.Group, item); - - return true; + return contextDict.AddSettingPageItem(item.Context, item.Section, item.Group, item); } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs index 472c44b..5f3eb3b 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs @@ -1,18 +1,21 @@ using System.Collections.Generic; -using WebExpress.WebCore.SettingPage; namespace WebExpress.WebCore.WebSettingPage.Model { + /// + /// Represents a context for setting page dictionary items, inheriting from Dictionary. + /// public class SettingPageDictionaryItemContext : Dictionary { /// - /// Adds a page. + /// Adds a setting page item to the dictionary. /// - /// The settig context. - /// The section. - /// The group. - /// The item to insert. - public void AddPage(string context, SettingSection section, string group, SettingPageItem page) + /// The setting context. + /// The section to which the item belongs. + /// The group to which the item belongs. + /// The setting page item to add. + /// True if the setting page item was added successfully; otherwise, false. + public bool AddSettingPageItem(string context, SettingSection section, string group, SettingPageItem page) { context ??= "*"; @@ -22,14 +25,14 @@ public void AddPage(string context, SettingSection section, string group, Settin Add(context, new SettingPageDictionaryItemSection()); } - this[context].AddPage(section, group, page); + return this[context].AddSettingPageItem(section, group, page); } /// - /// Searches for a setting page by its id. + /// Searches for a setting page by its ID. /// - /// The setting site. - /// The setting page found or null. + /// The ID of the setting page to search for. + /// The setting page found, or null if no page was found. public SettingPageSearchResult FindPage(string pageId) { foreach (var v in this) @@ -46,10 +49,10 @@ public SettingPageSearchResult FindPage(string pageId) } /// - /// Provide all sections that have the same setting context. + /// Provides all sections that have the same setting context. /// /// The setting context. - /// A listing of all sections of the same context. + /// A listing of all sections of the same context, or null if the context does not exist. public SettingPageDictionaryItemSection GetSections(string context) { if (ContainsKey(context)) diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs index 3fcf3f0..705eeae 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs @@ -1,35 +1,46 @@ using System; using System.Collections.Generic; using System.Linq; -using WebExpress.WebCore.SettingPage; namespace WebExpress.WebCore.WebSettingPage.Model { + /// + /// Represents a group of setting page dictionary items. + /// public class SettingPageDictionaryItemGroup : Dictionary> { /// - /// Adds a page. + /// Adds a setting page item to the specified group. /// - /// The group. - /// The item to insert. - public void AddPage(string group, SettingPageItem page) + /// The group to which the item should be added. + /// The setting page item to add. + /// True if the setting page item was added successfully; otherwise, false. + public bool AddSettingPageItem(string group, SettingPageItem page) { group ??= string.Empty; - // register group. + // Register group if it does not exist. if (!ContainsKey(group)) { Add(group, new List()); } - this[group].Add(page); + var list = this[group]; + + if (!list.Any(x => x.SettingPageContext.EndpointId == page.SettingPageContext.EndpointId)) + { + list.Add(page); + return true; + } + + return false; } /// - /// Searches for an item based on its Id. + /// Searches for a setting page item based on its ID. /// - /// The setting site. - /// The setting page found or null. + /// The ID of the setting page item to search for. + /// A containing the group and item if found; otherwise, null. public SettingPageSearchResult FindPage(string pageId) { foreach (var v in this) @@ -45,9 +56,9 @@ public SettingPageSearchResult FindPage(string pageId) } /// - /// Returns the first setting page. + /// Returns the first setting page item in the dictionary. /// - /// The first setting page. + /// A containing the group and first item if found; otherwise, null. public SettingPageSearchResult FindFirstPage() { var firstItem = default(SettingPageItem); @@ -66,10 +77,10 @@ public SettingPageSearchResult FindFirstPage() } /// - /// Returns all setting pages that are in the given group. + /// Returns all setting page items in the specified group. /// - /// The group. - /// A listing of all pages in the same group. + /// The group whose items should be returned. + /// A list of in the specified group; otherwise, null if the group does not exist. public List GetPages(string group) { if (ContainsKey(group)) diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs index 00c9dec..f165ce5 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs @@ -1,17 +1,17 @@ using System.Collections.Generic; -using WebExpress.WebCore.SettingPage; namespace WebExpress.WebCore.WebSettingPage.Model { public class SettingPageDictionaryItemSection : Dictionary { /// - /// Adds an item. + /// Adds an item to the specified section and group. /// - /// The section. - /// The group. + /// The section to add the item to. + /// The group within the section to add the item to. /// The item to insert. - public void AddPage(SettingSection section, string group, SettingPageItem item) + /// True if the settings page was added successfully; otherwise, false. + public bool AddSettingPageItem(SettingSection section, string group, SettingPageItem item) { // register Section if (!ContainsKey(section)) @@ -19,14 +19,14 @@ public void AddPage(SettingSection section, string group, SettingPageItem item) Add(section, new SettingPageDictionaryItemGroup()); } - this[section].AddPage(group, item); + return this[section].AddSettingPageItem(group, item); } /// - /// Searches for an item based on its id. + /// Searches for a setting page item based on its ID. /// - /// The setting site id. - /// The setting page found or null. + /// The ID of the setting page item to search for. + /// The setting page item found, or null if no item with the specified ID exists. public SettingPageSearchResult FindPage(string pageId) { foreach (var v in this) @@ -43,9 +43,9 @@ public SettingPageSearchResult FindPage(string pageId) } /// - /// Returns the first setting page. + /// Returns the first setting page item found. /// - /// The first setting page. + /// The first setting page item found, or null if no items exist. public SettingPageSearchResult FindFirstPage() { var firstPage = default(SettingPageSearchResult); @@ -85,10 +85,10 @@ public SettingPageSearchResult FindFirstPage() } /// - /// Returns a groups that are in the given section. + /// Returns the group of items that are in the specified section. /// - /// The section. - /// A group in the same section. + /// The section to retrieve the group from. + /// The group of items in the specified section, or null if the section does not exist. public SettingPageDictionaryItemGroup GetGroup(SettingSection section) { if (ContainsKey(section)) diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index ade9af4..63c5b03 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -1,5 +1,5 @@ using System; -using WebExpress.WebCore.SettingPage; +using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 9fe1cff..19d707a 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -6,7 +6,7 @@ using WebExpress.WebCore.WebSettingPage.Model; using WebExpress.WebCore.WebUri; -namespace WebExpress.WebCore.SettingPage +namespace WebExpress.WebCore.WebSettingPage { /// /// Interface representing the context of a setting page. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index d8f1454..ceec57c 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -10,10 +10,9 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebSettingPage.Model; -namespace WebExpress.WebCore.SettingPage +namespace WebExpress.WebCore.WebSettingPage { /// /// Management of settings pages. @@ -210,13 +209,15 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Represents the result of a search on the settings page. diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index e58b62a..91cc8c4 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -138,7 +138,7 @@ private void Register(IPluginContext pluginContext, IEnumerable()?.StatusCode == null) { From 632ad66e6bad0e7d3a9e39333bd1594264146604 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 3 Nov 2024 16:58:53 +0100 Subject: [PATCH 040/162] error fixes and code optimizations --- .../Manager/UnitTestSettingPageManager.cs | 135 ++++++++---------- .../TestSettingPageA.cs | 3 +- .../TestSettingPageB.cs | 3 +- src/WebExpress.WebCore/ArgumentParser.cs | 9 +- .../Setting/ISettingItem.cs | 3 + .../WebApplication/Application.cs | 5 +- .../WebApplication/ApplicationContext.cs | 7 +- .../WebAttribute/ISettingPageAttribute.cs | 9 ++ .../WebAttribute/ScopeAttribute.cs | 2 +- .../WebAttribute/TitleAttribute.cs | 2 +- .../WebEvent/Model/EventDictionary.cs | 23 ++- .../WebPage/IPageContext.cs | 4 +- .../WebSettingPage/ISettingPageContext.cs | 15 +- .../WebSettingPage/ISettingPageManager.cs | 16 +++ .../WebSettingPage/SettingPageContext.cs | 60 +++++++- .../WebSettingPage/SettingPageManager.cs | 37 ++++- .../WebStatusPage/StatusPageManager.cs | 43 ++---- .../WebUri/UriPathSegmentConstant.cs | 2 +- .../WebUri/UriPathSegmentRoot.cs | 30 ++-- src/WebExpress.WebCore/WebUri/UriResource.cs | 40 ++++-- 20 files changed, 279 insertions(+), 169 deletions(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/ISettingPageAttribute.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 95677ed..1217cd6 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -40,78 +40,69 @@ public void Remove() Assert.Empty(componentHub.SettingPageManager.SettingPages); } - ///// - ///// Test the id property of the setting page. - ///// - //[Theory] - //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - //public void Id(Type applicationType, Type resourceType, string id) - //{ - // // preconditions - // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); - - // // test execution - // Assert.Equal(id, settingPage.EndpointId); - //} - - ///// - ///// Test the title property of the setting page. - ///// - //[Theory] - //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "webindex:pagea.label")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "webindex:pageb.label")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "webindex:pagec.label")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "webindex:pagea.label")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "webindex:pageb.label")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "webindex:pagec.label")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "webindex:pagea.label")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "webindex:pageb.label")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "webindex:pagec.label")] - - //public void Title(Type applicationType, Type resourceType, string id) - //{ - // // preconditions - // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); - - // // test execution - // Assert.Equal(id, settingPage.PageTitle); - //} - - ///// - ///// Test the context path property of the setting page. - ///// - //[Theory] - //[InlineData(typeof(TestApplicationA), typeof(TestPageA), "/appa")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageB), "/appa/resa")] - //[InlineData(typeof(TestApplicationA), typeof(TestPageC), "/appa")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageA), "/appb")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageB), "/appb/resa")] - //[InlineData(typeof(TestApplicationB), typeof(TestPageC), "/appb")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageA), "/")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageB), "/resa")] - //[InlineData(typeof(TestApplicationC), typeof(TestPageC), "/")] - //public void ContextPath(Type applicationType, Type resourceType, string id) - //{ - // // preconditions - // var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - // var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - // var settingPage = componentHub.SettingPageManager.GetPages(resourceType, application)?.FirstOrDefault(); - - // // test execution - // Assert.Equal(id, settingPage.ContextPath); - //} + /// + /// Test the id property of the setting page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] + public void Id(Type applicationType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); + + // test execution + Assert.Equal(id, settingPage.EndpointId); + } + + /// + /// Test the title property of the setting page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "webindex:settingpagea.label")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "webindex:settingpageb.label")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "webindex:settingpagea.label")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "webindex:settingpageb.label")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webindex:settingpagea.label")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webindex:settingpageb.label")] + + public void Title(Type applicationType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); + + // test execution + Assert.Equal(id, settingPage.SettingPageTitle); + } + + /// + /// Test the context path property of the setting page. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/")] + public void ContextPath(Type applicationType, Type resourceType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); + + // test execution + Assert.Equal(id, settingPage.ContextPath); + } /// /// Tests whether the setting page manager implements interface IComponentManager. diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/TestSettingPageA.cs index 4d9a893..8ada1d4 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageA.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage; diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/TestSettingPageB.cs index bf8f202..e07954e 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageB.cs @@ -1,5 +1,4 @@ -using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage; diff --git a/src/WebExpress.WebCore/ArgumentParser.cs b/src/WebExpress.WebCore/ArgumentParser.cs index 87cbf83..8b473f7 100644 --- a/src/WebExpress.WebCore/ArgumentParser.cs +++ b/src/WebExpress.WebCore/ArgumentParser.cs @@ -26,10 +26,7 @@ public static ArgumentParser Current { get { - if (m_this == null) - { - m_this = new ArgumentParser(); - } + m_this ??= new ArgumentParser(); return m_this; } @@ -40,7 +37,7 @@ public static ArgumentParser Current /// public ArgumentParser() { - Commands = new List(); + Commands = []; } /// @@ -72,7 +69,7 @@ public ArguemtParserResult Parse(string[] args) if (s.StartsWith("--") == true) { } - else if (s.StartsWith("-") == true) + else if (s.StartsWith('-') == true) { if (!string.IsNullOrEmpty(key)) { diff --git a/src/WebExpress.WebCore/Setting/ISettingItem.cs b/src/WebExpress.WebCore/Setting/ISettingItem.cs index 5a3ad8f..eb2fdd5 100644 --- a/src/WebExpress.WebCore/Setting/ISettingItem.cs +++ b/src/WebExpress.WebCore/Setting/ISettingItem.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.Setting { + /// + /// Interface for a settings object. + /// public interface ISettingItem { } diff --git a/src/WebExpress.WebCore/WebApplication/Application.cs b/src/WebExpress.WebCore/WebApplication/Application.cs index 748aca5..98f63d6 100644 --- a/src/WebExpress.WebCore/WebApplication/Application.cs +++ b/src/WebExpress.WebCore/WebApplication/Application.cs @@ -1,4 +1,6 @@ -namespace WebExpress.WebCore.WebApplication +using System; + +namespace WebExpress.WebCore.WebApplication { /// /// This represents an application. @@ -38,6 +40,7 @@ public virtual void Run() /// public virtual void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs index e151e0c..267dbf2 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs @@ -3,6 +3,9 @@ namespace WebExpress.WebCore.WebApplication { + /// + /// Represents the context of an application. + /// public class ApplicationContext : IApplicationContext { /// @@ -53,12 +56,12 @@ public ApplicationContext() } /// - /// Conversion of the apllication context into its string representation. + /// Conversion of the application context into its string representation. /// /// The string that uniquely represents the application. public override string ToString() { - return $"{ApplicationId}"; + return $"Application: {ApplicationId}"; } } } diff --git a/src/WebExpress.WebCore/WebAttribute/ISettingPageAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ISettingPageAttribute.cs new file mode 100644 index 0000000..2ad2955 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ISettingPageAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a setting page assignment attribute. + /// + public interface ISettingPageAttribute : IEndpointAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs index 3d42cfa..7785831 100644 --- a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// The range in which the component is valid. /// [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] - public class ScopeAttribute : Attribute, IPageAttribute where T : class, IScope + public class ScopeAttribute : Attribute, IPageAttribute, ISettingPageAttribute where T : class, IScope { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs index c7d4826..2145557 100644 --- a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs @@ -1,6 +1,6 @@ namespace WebExpress.WebCore.WebAttribute { - public class TitleAttribute : System.Attribute, IPageAttribute, IStatusPageAttribute + public class TitleAttribute : System.Attribute, IPageAttribute, ISettingPageAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs index d012068..7c51b29 100644 --- a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs @@ -27,28 +27,25 @@ public bool AddEventItem(IPluginContext pluginContext, IApplicationContext appli return false; } - if (!ContainsKey(pluginContext)) + if (!TryGetValue(pluginContext, out var appContextDict)) { - this[pluginContext] = []; + appContextDict = []; + this[pluginContext] = appContextDict; } - var appContextDict = this[pluginContext]; - - if (!appContextDict.ContainsKey(applicationContext)) + if (!appContextDict.TryGetValue(applicationContext, out var eventDict)) { - appContextDict[applicationContext] = []; + eventDict = []; + appContextDict[applicationContext] = eventDict; } - var eventDict = appContextDict[applicationContext]; - - if (!eventDict.ContainsKey(type)) + if (!eventDict.TryGetValue(type, out var eventList)) { - eventDict[type] = []; + eventList = []; + eventDict[type] = eventList; } - var eventList = eventDict[type]; - - if (eventList.Where(x => x.EventClass == type).Any()) + if (eventList.Any(x => x.EventClass == type)) { return false; // item with the same event handler already exists } diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index b13a867..3c6f18d 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -9,12 +9,12 @@ namespace WebExpress.WebCore.WebPage public interface IPageContext : IEndpointContext { /// - /// Returns the resource title. + /// Returns the page title. /// string PageTitle { get; } /// - /// Returns the scope names that provides the resource. The scope name + /// Returns the scope names that provides the page. The scope name /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index d79c205..96f6177 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebEndpoint; +using System.Collections.Generic; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage @@ -9,6 +10,18 @@ namespace WebExpress.WebCore.WebSettingPage /// public interface ISettingPageContext : IEndpointContext { + /// + /// Returns the setting page title. + /// + string SettingPageTitle { get; } + + /// + /// Returns the scope names that provides the setting page. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + IEnumerable Scopes { get; } + /// /// Returns the group to which the setting page belongs. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs index 5f7dc3e..deb38d3 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebSettingPage @@ -23,5 +24,20 @@ public interface ISettingPageManager : IComponentManager /// Returns the collection of setting pages. /// IEnumerable SettingPages { get; } + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// An enumeration of setting page contextes. + IEnumerable GetSettingPages(Type settingPageType); + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// The context of the application. + /// An enumeration of setting page contextes. + IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext); } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 19d707a..38d9f92 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; @@ -14,6 +16,11 @@ namespace WebExpress.WebCore.WebSettingPage /// public class SettingPageContext : ISettingPageContext { + private readonly IEndpointManager _endpointManager; + private readonly Type _parentType; + private readonly UriResource _contextPath; + private readonly IUriPathSegment _pathSegment; + /// /// Returns the context of the associated plugin. /// @@ -29,6 +36,18 @@ public class SettingPageContext : ISettingPageContext /// public string EndpointId { get; internal set; } + /// + /// Returns the setting page title. + /// + public string SettingPageTitle { get; internal set; } + + /// + /// Returns the scope names that provides the setting page. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + public IEnumerable Scopes { get; internal set; } = []; + /// /// Returns the group to which the setting page belongs. /// @@ -67,22 +86,49 @@ public class SettingPageContext : ISettingPageContext /// /// Returns the parent context of the endpoint. /// - public IEndpointContext ParentContext { get; internal set; } + public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) + .FirstOrDefault(); /// /// Returns a value indicating whether to include sub-paths. /// public bool IncludeSubPaths { get; internal set; } - /// - /// Returns the context path of the URI resource. - /// - public UriResource ContextPath { get; internal set; } + /// + /// Returns the context path. + /// + public UriResource ContextPath + { + get + { + var parentContext = ParentContext; + if (parentContext != null) + { + return UriResource.Combine(ParentContext?.Uri, _contextPath); + } + + return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); + } + } /// /// Returns the URI of the setting page. /// - public UriResource Uri { get; internal set; } + public UriResource Uri => ContextPath.Append(_pathSegment); + /// + /// Initializes a new instance of the class with the specified parent type and context path. + /// + /// The endpoint manager responsible for managing endpoints. + /// The type of the parent resource. + /// The context path of the resource. + /// The path segment of the resource. + public SettingPageContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) + { + _endpointManager = endpointManager; + _parentType = parentType; + _contextPath = contextPath; + _pathSegment = pathSegment; + } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index ceec57c..97434ce 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -11,6 +11,7 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSettingPage.Model; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSettingPage { @@ -173,6 +174,11 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass && x.IsSealed && (x.GetInterfaces().Contains(typeof(ISettingPage))))) { var id = settingPageType.FullName?.ToLower(); + var title = settingPageType.Name; + var segment = default(ISegmentAttribute); + var parent = default(Type); + var contextPath = string.Empty; + var scopes = new List(); var context = default(string); var group = default(string); var section = SettingSection.Primary; @@ -184,7 +190,19 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { - if (customAttribute.AttributeType == typeof(SettingContextAttribute)) + if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) + { + segment = settingPageType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; + } + else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) + { + parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + } + else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) + { + contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(SettingContextAttribute)) { context = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } @@ -210,14 +228,29 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(ISettingPageAttribute)))) + { + if (customAttribute.AttributeType == typeof(TitleAttribute)) + { + title = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) + { + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + } + } + // assign the fragment to existing applications foreach (var applicationContext in applicationContexts) { - var settingPageContext = new SettingPageContext() + var settingPageContext = new SettingPageContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) { ApplicationContext = applicationContext, PluginContext = pluginContext, EndpointId = id, + SettingPageTitle = title, + Scopes = scopes, Context = context, Section = section, Group = group, diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 91cc8c4..704c6f5 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -135,8 +135,6 @@ private void Register(IPluginContext pluginContext, IEnumerable - /// Discovers and registers entries from the specified plugin. - /// - /// A list with plugin contexts that contain the status pages. - private void Register(IEnumerable pluginContexts) - { - foreach (var pluginContext in pluginContexts) - { - Register(pluginContext); - } + Log(); } /// @@ -439,23 +427,20 @@ private void OnAddApplication(object sender, IApplicationContext e) /// /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { - foreach (var statusCode in _dictionary.GetStatusPageContexts(pluginContext).Select(x => x.StatusCode)) - { - output.Add - ( - string.Empty.PadRight(4) + - I18N.Translate - ( - "webexpress:statuspagemanager.statuspage", - statusCode - ) - ); - } + //foreach (var statusCode in _dictionary.GetStatusPageContexts(pluginContext).Select(x => x.StatusCode)) + //{ + // output.Add + // ( + // string.Empty.PadRight(4) + + // I18N.Translate + // ( + // "webexpress:statuspagemanager.statuspage", + // statusCode + // ) + // ); + //} } /// diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs index 5503b11..9abbc45 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentConstant.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebUri { /// - /// constant path segment. + /// Constant path segment. /// public class UriPathSegmentConstant : IUriPathSegmentConstant { diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs index 7552a8c..a27075c 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentRoot.cs @@ -5,12 +5,12 @@ namespace WebExpress.WebCore.WebUri { /// - /// constant path segment. + /// Represents the root segment of a URI path. /// public class UriPathSegmentRoot : IUriPathSegment { /// - /// Returns or sets the id. + /// Returns the ID of the segment. /// public string Id => "ROOT"; @@ -30,16 +30,15 @@ public class UriPathSegmentRoot : IUriPathSegment public object Tag { get; set; } /// - /// Checks for empty path segment. + /// Returns a value indicating whether the path segment is empty. /// public bool IsEmpty => false; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The name. /// The display text. - /// The tag or null + /// The tag or null. public UriPathSegmentRoot(string display = null, object tag = null) { Value = "/"; @@ -48,10 +47,10 @@ public UriPathSegmentRoot(string display = null, object tag = null) } /// - /// Checks whether the node matches the path element. + /// Checks whether the node matches the specified path element. /// /// The value to check. - /// True if the path element matched, false otherwise. + /// True if the path element matches, false otherwise. public bool IsMatched(string value) { if (string.IsNullOrWhiteSpace(value)) @@ -64,19 +63,19 @@ public bool IsMatched(string value) } /// - /// Make a deep copy. + /// Creates a deep copy of the current segment. /// - /// The copy. + /// A copy of the current segment. public virtual IUriPathSegment Copy() { return new UriPathSegmentRoot(Display, Tag); } /// - /// Compare the object. + /// Compares the current segment with another object. /// - /// The comparison object. - /// true if equals, false otherwise + /// The object to compare with. + /// True if the objects are equal, false otherwise. public virtual bool Equals(IUriPathSegment obj) { if (obj == null) @@ -84,13 +83,14 @@ public virtual bool Equals(IUriPathSegment obj) return false; } - return obj is UriPathSegmentRoot segment; + return obj is UriPathSegmentRoot; } /// - /// Returns or sets the display text. + /// Returns the display text for the specified culture. /// /// The culture. + /// The display text for the specified culture. public virtual string GetDisplay(CultureInfo culture) { return I18N.Translate(culture, Display); diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriResource.cs index 56acc02..12a6333 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriResource.cs @@ -8,8 +8,22 @@ namespace WebExpress.WebCore.WebUri /// /// A resource uri (e.g. /image.png). /// - public class UriResource + public partial class UriResource { + /// + /// A regular expression to match URIs. + /// + /// A Regex object for matching URIs. + [GeneratedRegex("^([a-z0-9+.-]+):(?://(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\\d*))?(.*)?)$")] + private static partial Regex UriRegex(); + + /// + /// Regular expression to match relative URIs. + /// + /// A Regex object for matching relative URIs. + [GeneratedRegex(@"^(\/([a-zA-Z0-9-+*%()=._/$]*))(#([a-zA-Z0-9-+*%()=._/$]*))?(\?(.*))?$")] + private static partial Regex RelativeUriRegex(); + /// /// The scheme (e.g. Http, FTP). /// @@ -23,7 +37,7 @@ public class UriResource /// /// The path (e.g. /over/there). /// - public ICollection PathSegments { get; } = new List(); + public ICollection PathSegments { get; } = []; /// /// Returns the extended path. The extended path is the postfix of the resource's path. @@ -32,14 +46,14 @@ public UriResource ExtendedPath { get { - return new UriResource(Skip(EndpointRoot.PathSegments.Count()).PathSegments?.ToArray()); + return new UriResource(Skip(EndpointRoot.PathSegments.Count).PathSegments?.ToArray()); } } /// /// The query part (e.g. ?title=Uniform_Resource_Identifier&action=submit). /// - public ICollection Query { get; } = new List(); + public ICollection Query { get; } = []; /// /// References a position within a resource (e.g. #Anchor). @@ -73,7 +87,7 @@ public virtual string Display /// /// Determines if the uri is empty. /// - public bool Empty => !PathSegments.Any(); + public bool Empty => PathSegments.Count == 0; /// /// Returns the root of the endpoint. @@ -93,7 +107,7 @@ public virtual string Display /// /// Determines if the Uri is the root. /// - public bool IsRoot => PathSegments.Count() == 1; + public bool IsRoot => PathSegments.Count == 1; /// /// Checks if it is a relative uri. @@ -155,7 +169,7 @@ public UriResource(string uri) if (Enum.GetNames(typeof(UriScheme)).Where(x => uri.StartsWith(x, StringComparison.OrdinalIgnoreCase)).Any()) { - var match = Regex.Match(uri, "^([a-z0-9+.-]+):(?://(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\\d*))?(.*)?)$"); + var match = UriRegex().Match(uri); try { @@ -177,7 +191,7 @@ public UriResource(string uri) } - var relativeMatch = Regex.Match(uri, @"^(\/([a-zA-Z0-9-+*%()=._/$]*))(#([a-zA-Z0-9-+*%()=._/$]*))?(\?(.*))?$"); + var relativeMatch = RelativeUriRegex().Match(uri); PathSegments.Add(new UriPathSegmentRoot()); @@ -220,7 +234,7 @@ public UriResource(params IUriPathSegment[] segments) { PathSegments.Add(new UriPathSegmentRoot()); - foreach (var segment in segments.Where(x => !(x is UriPathSegmentRoot))) + foreach (var segment in segments.Where(x => x is not UriPathSegmentRoot)) { PathSegments.Add(segment); } @@ -267,7 +281,7 @@ public UriResource(UriScheme scheme, UriAuthority authority, string fragment, IE Authority = authority; PathSegments.Add(new UriPathSegmentRoot()); - foreach (var segment in segments != null ? segments.Where(x => !(x is UriPathSegmentRoot)) : Enumerable.Empty()) + foreach (var segment in segments != null ? segments.Where(x => x is not UriPathSegmentRoot) : []) { PathSegments.Add(segment.Copy()); } @@ -448,7 +462,7 @@ public override string ToString() var uri = "/" + string.Join ( "/", - PathSegments.Where(x => !(x is UriPathSegmentRoot)).Select(x => x.ToString()) + PathSegments.Where(x => x is not UriPathSegmentRoot).Select(x => x.ToString()) ); if (!string.IsNullOrWhiteSpace(Fragment)) @@ -456,7 +470,7 @@ public override string ToString() uri += "#" + Fragment; } - if (Query.Any()) + if (Query.Count != 0) { uri += "?" + string.Join("&", Query.Select(x => $"{x.Key}={x.Value}")); } @@ -520,5 +534,7 @@ public static implicit operator string(UriResource uri) { return uri?.ToString(); } + + } } \ No newline at end of file From 8f8513e88ce0f4fed7d6c6527ccc797478e845d9 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 25 Nov 2024 00:09:40 +0100 Subject: [PATCH 041/162] #5 added identity manager --- .../Data/MockIdentity.cs | 63 ++ .../Data/MockIdentityFactory.cs | 119 ++++ .../Data/MockIdentityGroup.cs | 47 ++ .../Fixture/UnitTestControlFixture.cs | 3 +- .../Manager/UnitTestIdentityManager.cs | 211 +++++++ .../TestIdentityPermissionA.cs | 21 + .../TestIdentityPermissionB.cs | 21 + .../TestIdentityPermissionC.cs | 21 + .../TestIdentityRoleA.cs | 20 + .../TestIdentityRoleB.cs | 22 + .../WebAttribute/IPermissionAttribute.cs | 9 + .../WebAttribute/IRoleAttribute.cs | 9 + .../WebAttribute/PermissionAttribute.cs | 20 + .../WebAttribute/RoleAttribute.cs | 20 + .../WebComponent/ComponentActivator.cs | 44 +- .../WebComponent/ComponentHub.cs | 12 +- .../WebComponent/IComponentHub.cs | 9 +- .../WebEvent/EventHandlerContext.cs | 9 + .../WebEvent/Model/EventItem.cs | 2 +- .../WebIdentity/IIdentity.cs | 36 ++ .../WebIdentity/IIdentityGroup.cs | 15 + .../WebIdentity/IIdentityManager.cs | 110 ++++ .../WebIdentity/IIdentityPermission.cs | 12 + .../WebIdentity/IIdentityPermissionContext.cs | 33 + .../WebIdentity/IIdentityResource.cs | 9 + .../WebIdentity/IIdentityRole.cs | 11 + .../WebIdentity/IIdentityRoleContext.cs | 27 + .../WebIdentity/IdentityManager.cs | 584 ++++++++++++++++++ .../WebIdentity/IdentityPermissionContext.cs | 41 ++ .../WebIdentity/IdentityRoleContext.cs | 35 ++ .../WebIdentity/Model/IdentityItem.cs | 9 + .../Model/IdentityPermissionDictionary.cs | 138 +++++ .../Model/IdentityPermissionItem.cs | 84 +++ .../Model/IdentityRoleDictionary.cs | 153 +++++ .../WebIdentity/Model/IdentityRoleItem.cs | 84 +++ .../WebMessage/RequestHeaderFields.cs | 2 +- .../WebSession/Model/Session.cs | 44 +- .../Model/SessionPropertyAuthentification.cs | 19 +- .../Model/SessionPropertyAuthorization.cs | 1 + 39 files changed, 2112 insertions(+), 17 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Data/MockIdentity.cs create mode 100644 src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs create mode 100644 src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestIdentityPermissionA.cs create mode 100644 src/WebExpress.WebCore.Test/TestIdentityPermissionB.cs create mode 100644 src/WebExpress.WebCore.Test/TestIdentityPermissionC.cs create mode 100644 src/WebExpress.WebCore.Test/TestIdentityRoleA.cs create mode 100644 src/WebExpress.WebCore.Test/TestIdentityRoleB.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IPermissionAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IRoleAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/PermissionAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/RoleAttribute.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentity.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityGroup.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityPermission.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityResource.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityRole.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IdentityManager.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/Model/IdentityItem.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs create mode 100644 src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentity.cs b/src/WebExpress.WebCore.Test/Data/MockIdentity.cs new file mode 100644 index 0000000..175466b --- /dev/null +++ b/src/WebExpress.WebCore.Test/Data/MockIdentity.cs @@ -0,0 +1,63 @@ +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test.Data +{ + /// + /// Represents an identity object that implements the IIdentity interface. + /// + internal class MockIdentity : IIdentity + { + private readonly List _groups = new(); + + /// + /// Returns or sets the id of the user. + /// + public Guid Id { get; } + + /// + /// Returns or sets the name of the user. + /// + public string Name { get; } + + /// + /// Returns or sets the email of the user. + /// + public string Email { get; } + + /// + /// Returns the hash of the password. + /// + public string PasswordHash { get; } + + /// + /// Returns the groups associated with the user. + /// + public IEnumerable Groups => _groups; + + /// + /// Initializes a new instance of the class with the specified id, name, email, and password. + /// + /// The id of the user. + /// The name of the user. + /// The email of the user. + /// The password hash of the user. + public MockIdentity(Guid id, string name, string email, string passwordHash) + { + Id = id; + Name = name; + Email = email; + PasswordHash = passwordHash; + } + + /// + /// Assigns groups. + /// + /// The list of groups to assign users to. + public void Assign(IEnumerable groups) + { + _groups.AddRange(groups); + } + + + } +} diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs new file mode 100644 index 0000000..68a9b71 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs @@ -0,0 +1,119 @@ +using System.Security; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test.Data +{ + /// + /// A factory class for creating mock identities for test users and groups. + /// + internal class MockIdentityFactory + { + private static readonly IEnumerable _groups = CreateTestGroups(); + private static readonly IEnumerable _users = CreateTestUsers(); + + /// + /// Retrieves a mock identity based on the provided name. + /// + /// The name of the identity to retrieve. + /// The mock identity with the specified name, or null if not found. + public static IIdentity GetIdentity(string name) + { + return _users.FirstOrDefault(x => x.Name == name); + } + + /// + /// Retrieves a mock identitygroup based on the provided name. + /// + /// The name of the identity group to retrieve. + /// The mock identity group with the specified name, or null if not found. + public static IIdentityGroup GetIdentityGroup(string name) + { + return _groups.FirstOrDefault(x => x.Name == name); + } + + /// + /// Creates a list of test groups with mock identities. + /// + /// A list of identity groups. + private static IEnumerable CreateTestGroups() + { + var group1 = new MockIdentityGroup(id: Guid.NewGuid(), name: "Admins"); + group1.Assign(["webexpress.webcore.test.testidentityrolea", "webexpress.webcore.test.testidentityroleb"]); + + yield return group1; + + var group2 = new MockIdentityGroup(id: Guid.NewGuid(), name: "Users"); + group2.Assign(["webexpress.webcore.test.testidentityroleb"]); + + yield return group2; + + var group3 = new MockIdentityGroup(id: Guid.NewGuid(), name: "Guests"); + group3.Assign([]); + + yield return group3; + } + + /// + /// Creates a list of test users with mock identities. + /// + /// A list of identitys. + private static IEnumerable CreateTestUsers() + { + var passwort = new SecureString(); + passwort.AppendChar('a'); + passwort.AppendChar('b'); + passwort.AppendChar('c'); + passwort.MakeReadOnly(); + + var user = new MockIdentity(Guid.NewGuid(), "Alice", "alice@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(0)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Bob", "bob@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Charlie", "charlie@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(2)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "David", "david@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1), _groups.ElementAt(2)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Eve", "eve@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Frank", "frank@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Grace", "grace@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Heidi", "heidi@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Ivan", "ivan@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + + user = new MockIdentity(Guid.NewGuid(), "Judy", "judy@example.com", IdentityManager.ComputeHash(passwort)); + user.Assign([_groups.ElementAt(1)]); + + yield return user; + } + } +} diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs new file mode 100644 index 0000000..abcd0ef --- /dev/null +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs @@ -0,0 +1,47 @@ +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test.Data +{ + /// + /// Represents a group identity with an ID and a name. + /// + internal class MockIdentityGroup : IIdentityGroup + { + private readonly List _roles = []; + + /// + /// Returns or sets the id of the group. + /// + public Guid Id { get; set; } + + /// + /// Returns or sets the name of the group. + /// + public string Name { get; set; } + + /// + /// Returns the roles associated with the group. + /// + public IEnumerable Roles => _roles; + + /// + /// Initializes a new instance of the class with the specified id and name. + /// + /// The id of the group. + /// The name of the group. + public MockIdentityGroup(Guid id, string name) + { + Id = id; ; + Name = name; + } + + /// + /// Assigns roles. + /// + /// The list of roles to assign group to. + public void Assign(IEnumerable roles) + { + _roles.AddRange(roles); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index 90e3cec..d727d60 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -145,7 +145,8 @@ public static WebMessage.HttpContext CreateHttpContextMock(string content = "") ["AcceptEncoding"] = "gzip, deflate, br, zstd", ["AcceptLanguage"] = "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", - ["Referer"] = "0HN50661TV8TP" + ["Referer"] = "0HN50661TV8TP", + ["Cookie"] = "session=AB333C76-E73F-45E0-85FD-123320D9B85F" }, Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, Method = firstLine.Split(' ')?.Where(x => !string.IsNullOrEmpty(x)).FirstOrDefault() ?? "GET", diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs new file mode 100644 index 0000000..bae1660 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs @@ -0,0 +1,211 @@ +using System.Security; +using WebExpress.WebCore.Test.Data; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the identity manager. + /// + [Collection("NonParallelTests")] + public class UnitTestIdentityManager + { + /// + /// Test the register function of the identity manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(9, componentHub.IdentityManager.Permissions.Count()); + Assert.Equal(6, componentHub.IdentityManager.Roles.Count()); + } + + /// + /// Test the remove function of the identity manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + var identityManager = componentHub.IdentityManager as IdentityManager; + + // test execution + identityManager.Remove(plugin); + + Assert.Empty(componentHub.IdentityManager.Permissions); + Assert.Empty(componentHub.IdentityManager.Roles); + } + + /// + /// Tests whether the identity manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.IdentityManager.GetType())); + } + + /// + /// Test the CheckAccess function of the identity manager. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "Alice", typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), "Alice", typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), "Alice", typeof(TestIdentityPermissionC), true)] + [InlineData(typeof(TestApplicationA), "Bob", typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), "Bob", typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), "Bob", typeof(TestIdentityPermissionC), false)] + [InlineData(typeof(TestApplicationA), "Charlie", typeof(TestIdentityPermissionA), false)] + [InlineData(typeof(TestApplicationA), "Charlie", typeof(TestIdentityPermissionB), false)] + [InlineData(typeof(TestApplicationA), "Charlie", typeof(TestIdentityPermissionC), false)] + public void CheckAccessIdentity(Type application, string identityName, Type permission, bool expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); + var identity = MockIdentityFactory.GetIdentity(identityName); + + // test execution + var access = identityManager.CheckAccess(applicationContext, identity, permission); + + Assert.Equal(expected, access); + } + + /// + /// Test the CheckAccess function of the identity manager. + /// + [Theory] + [InlineData(typeof(TestApplicationA), "Admins", typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), "Admins", typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), "Admins", typeof(TestIdentityPermissionC), true)] + [InlineData(typeof(TestApplicationA), "Users", typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), "Users", typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), "Users", typeof(TestIdentityPermissionC), false)] + [InlineData(typeof(TestApplicationA), "Guests", typeof(TestIdentityPermissionA), false)] + [InlineData(typeof(TestApplicationA), "Guests", typeof(TestIdentityPermissionB), false)] + [InlineData(typeof(TestApplicationA), "Guests", typeof(TestIdentityPermissionC), false)] + public void CheckAccessGroup(Type application, string groupName, Type permission, bool expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); + var group = MockIdentityFactory.GetIdentityGroup(groupName); + + // test execution + var access = identityManager.CheckAccess(applicationContext, group, permission); + + Assert.Equal(expected, access); + } + + /// + /// Test the CheckAccess function of the identity manager. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleA), typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleA), typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleA), typeof(TestIdentityPermissionC), true)] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleB), typeof(TestIdentityPermissionA), true)] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleB), typeof(TestIdentityPermissionB), true)] + [InlineData(typeof(TestApplicationA), typeof(TestIdentityRoleB), typeof(TestIdentityPermissionC), false)] + public void CheckAccessRole(Type application, Type role, Type permission, bool expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); + + // test execution + var access = identityManager.CheckAccess(applicationContext, role, permission); + + Assert.Equal(expected, access); + } + + /// + /// Test the Login function of the identity manager. + /// + [Theory] + [InlineData("Alice", "abc", true)] + [InlineData("Alice", "123", false)] + public void Login(string identityName, string password, bool expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var request = UnitTestControlFixture.CrerateRequestMock(); + var identity = MockIdentityFactory.GetIdentity(identityName); + var securePassword = new SecureString(); + password.ToList().ForEach(x => securePassword.AppendChar(x)); + securePassword.MakeReadOnly(); + + // test execution + var res = identityManager.Login(request, identity, securePassword); + + Assert.Equal(expected, res); + } + + /// + /// Test the Login function of the identity manager. + /// + [Theory] + [InlineData("Alice", "abc")] + [InlineData("Bob", "abc")] + [InlineData("Charlie", "abc")] + public void Logout(string identityName, string password) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var request = UnitTestControlFixture.CrerateRequestMock(); + var identity = MockIdentityFactory.GetIdentity(identityName); + var securePassword = new SecureString(); + password.ToList().ForEach(x => securePassword.AppendChar(x)); + securePassword.MakeReadOnly(); + identityManager.Login(request, identity, securePassword); + + // test execution + identityManager.Logout(request); + + var res = identityManager.GetCurrentIdentity(request); + Assert.Null(res); + } + + /// + /// Test the GetCurrentIdentity function of the identity manager. + /// + [Theory] + [InlineData("Alice", "abc")] + [InlineData("Bob", "abc")] + [InlineData("Charlie", "abc")] + public void GetCurrentIdentity(string identityName, string password) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var identityManager = componentHub.IdentityManager as IdentityManager; + var request = UnitTestControlFixture.CrerateRequestMock(); + var identity = MockIdentityFactory.GetIdentity(identityName); + var securePassword = new SecureString(); + password.ToList().ForEach(x => securePassword.AppendChar(x)); + securePassword.MakeReadOnly(); + identityManager.Login(request, identity, securePassword); + + // test execution + var res = identityManager.GetCurrentIdentity(request); + + Assert.Equal(identity, res); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIdentityPermissionA.cs b/src/WebExpress.WebCore.Test/TestIdentityPermissionA.cs new file mode 100644 index 0000000..a35920b --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIdentityPermissionA.cs @@ -0,0 +1,21 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy permission. + /// + [Name("Read")] + [Description("Permissions to read.")] + [Role()] + public sealed class TestIdentityPermissionA : IIdentityPermission + { + /// + /// Releases all resources used by the current instance of the class. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIdentityPermissionB.cs b/src/WebExpress.WebCore.Test/TestIdentityPermissionB.cs new file mode 100644 index 0000000..d45da1c --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIdentityPermissionB.cs @@ -0,0 +1,21 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy permission. + /// + [Name("Write")] + [Description("Permissions to write.")] + [Role()] + public sealed class TestIdentityPermissionB : IIdentityPermission + { + /// + /// Releases all resources used by the current instance of the class. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIdentityPermissionC.cs b/src/WebExpress.WebCore.Test/TestIdentityPermissionC.cs new file mode 100644 index 0000000..d20e621 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIdentityPermissionC.cs @@ -0,0 +1,21 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy permission. + /// + [Name("Delte")] + [Description("Permissions to delete.")] + [Role()] + public sealed class TestIdentityPermissionC : IIdentityPermission + { + /// + /// Releases all resources used by the current instance of the class. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIdentityRoleA.cs b/src/WebExpress.WebCore.Test/TestIdentityRoleA.cs new file mode 100644 index 0000000..c10ddf6 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIdentityRoleA.cs @@ -0,0 +1,20 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy role. + /// + [Name("Admin")] + [Description("Has permissions to create, edit, and delete data.")] + public sealed class TestIdentityRoleA : IIdentityRole + { + /// + /// Releases all resources used by the current instance of the class. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIdentityRoleB.cs b/src/WebExpress.WebCore.Test/TestIdentityRoleB.cs new file mode 100644 index 0000000..0f6abf4 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIdentityRoleB.cs @@ -0,0 +1,22 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy role. + /// + [Name("Editor")] + [Description("Has permissions to create and edit, but not delete.")] + [Permission()] + [Permission()] + public sealed class TestIdentityRoleB : IIdentityRole + { + /// + /// Releases all resources used by the current instance of the class. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IPermissionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IPermissionAttribute.cs new file mode 100644 index 0000000..6dc2788 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IPermissionAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a permission assignment attribute. + /// + public interface IPermissionAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IRoleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IRoleAttribute.cs new file mode 100644 index 0000000..f868701 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IRoleAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a role assignment attribute. + /// + public interface IRoleAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/PermissionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/PermissionAttribute.cs new file mode 100644 index 0000000..704de81 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/PermissionAttribute.cs @@ -0,0 +1,20 @@ +using System; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Connects roles with permissions. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class PermissionAttribute : Attribute, IPermissionAttribute where T : class, IIdentityPermission + { + /// + /// Initializes a new instance of the class. + /// + public PermissionAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/RoleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/RoleAttribute.cs new file mode 100644 index 0000000..8970557 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/RoleAttribute.cs @@ -0,0 +1,20 @@ +using System; +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Connects roles with permissions. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class RoleAttribute : Attribute, IRoleAttribute where T : class, IIdentityRole + { + /// + /// Initializes a new instance of the class. + /// + public RoleAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index ee6debf..23e829d 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -59,8 +59,8 @@ public static T CreateInstance(Type responseType, IHttpServerContext httpServ /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// /// The type of the component manager, which must implement . - /// The reference to the context of the host. /// The type of the component to create. + /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. @@ -97,6 +97,48 @@ public static T CreateInstance(Type componentType, IHttpServerContext httpSer return Activator.CreateInstance(componentType) as T; } + /// + /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. + /// + /// The type of the component manager, which must implement . + /// The reference to the context of the host. + /// The component hub to use for dependency injection. + /// The type of the component to create. + /// Additional parameters to pass to the component's constructor. + /// An instance of the specified component type. + public static T CreateInstance(IHttpServerContext httpServerContext, IComponentHub componentHub, Type componentType, params object[] advancedParameters) where T : IComponent + { + var flags = BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = componentType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var parameters = constructor.GetParameters(); + var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : + properties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return (T)Activator.CreateInstance(componentType); + } + /// /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 727189a..7d2e573 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebComponent.Model; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebIdentity; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPackage; @@ -16,6 +16,7 @@ using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebRestApi; using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebTask; @@ -46,6 +47,7 @@ public class ComponentHub : IComponentHub private readonly EventManager _eventManager; private readonly JobManager _jobManager; private readonly TaskManager _taskManager; + private readonly IdentityManager _identityManager; /// /// An event that fires when an component is added. @@ -77,6 +79,7 @@ public class ComponentHub : IComponentHub _jobManager, _statusPageManager, _internationalizationManager, + _identityManager, _sessionManager, _taskManager }.Concat(_dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); @@ -177,6 +180,12 @@ public class ComponentHub : IComponentHub /// The instance of the internationalization manager. public IInternationalizationManager InternationalizationManager => _internationalizationManager; + /// + /// Returns the identity manager. + /// + /// The instance of the identity manager. + public IIdentityManager IdentityManager => _identityManager; + /// /// Returns the session manager. /// @@ -210,6 +219,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _jobManager = CreateInstance(typeof(JobManager)) as JobManager; _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; _taskManager = CreateInstance(typeof(TaskManager)) as TaskManager; + _identityManager = CreateInstance(typeof(IdentityManager)) as IdentityManager; _internationalizationManager.Register(typeof(HttpServer).Assembly, "webexpress"); diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index bc39f80..5e008f1 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebIdentity; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPackage; @@ -14,6 +14,7 @@ using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebRestApi; using WebExpress.WebCore.WebSession; +using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebTask; @@ -136,6 +137,12 @@ public interface IComponentHub : IComponentManager /// The instance of the internationalization manager. IInternationalizationManager InternationalizationManager { get; } + /// + /// Returns the identity manager. + /// + /// The instance of the identity manager. + IIdentityManager IdentityManager { get; } + /// /// Returns the session manager. /// diff --git a/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs index 73a0468..f72260e 100644 --- a/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs +++ b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs @@ -27,5 +27,14 @@ public class EventHandlerContext : IEventHandlerContext /// Returns the corresponding application context. /// public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Event: {EventId}"; + } } } diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs index 91ff472..180a4d4 100644 --- a/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs @@ -119,7 +119,7 @@ public void Dispose() /// The event element in its string representation. public override string ToString() { - return $"Event '{EventClass.FullName.ToLower()}'"; + return $"Event: '{EventClass.FullName.ToLower()}'"; } } } diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentity.cs b/src/WebExpress.WebCore/WebIdentity/IIdentity.cs new file mode 100644 index 0000000..547d7a5 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentity.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Represents an identity in the web application. + /// + public interface IIdentity + { + /// + /// Returns the id of the identity. + /// + public Guid Id { get; } + + /// + /// Returns the name of the identity. + /// + public string Name { get; } + + /// + /// Returns the email of the identity. + /// + public string Email { get; } + + /// + /// Returns the hash of the password. + /// + string PasswordHash { get; } + + /// + /// Returns the groups associated with the identity. + /// + IEnumerable Groups { get; } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityGroup.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityGroup.cs new file mode 100644 index 0000000..d8a98a8 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityGroup.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Interface that defines an identity group. + /// + public interface IIdentityGroup + { + /// + /// Returns the roles associated with the group. + /// + IEnumerable Roles { get; } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs new file mode 100644 index 0000000..af1a5e0 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Security; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Interface for managing identities. + /// + public interface IIdentityManager : IComponentManager + { + /// + /// Returns all permissions. + /// + public IEnumerable Permissions { get; } + + /// + /// Returns all roles. + /// + public IEnumerable Roles { get; } + + /// + /// Returns all identities. + /// + IEnumerable Identities { get; } + + /// + /// Returns the current signed-in identity. + /// + IIdentity CurrentIdentity { get; } + + /// + /// Login an identity. + /// + /// The request. + /// The identity. + /// The password. + /// True if successful, false otherwise. + bool Login(Request request, IIdentity identity, SecureString password); + + /// + /// Logout an identity. + /// + /// The request. + void Logout(Request request); + + /// + /// Returns the current signed-in identity based on the provided request. + /// + /// The request to get the current identity for. + /// The current signed-in identity. + IIdentity GetCurrentIdentity(Request request); + + /// + /// Checks if the specified identity has the given permission. + /// + /// The type of the identity permission. + /// The context of the application. + /// The identity to check. + /// True if the identity has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext, IIdentity identity) where T : IIdentityPermission; + + /// + /// Checks if the specified identity has the given permission. + /// + /// The context of the application. + /// The identity to check. + /// The permission to check for. + /// True if the identity has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext, IIdentity identity, Type permission); + + /// + /// Checks if the specified identity group has the given permission. + /// + /// The type of the identity permission. + /// The context of the application. + /// The identity group to check. + /// True if the identity group has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group) where T : IIdentityPermission; + + /// + /// Checks if the specified identity group has the given permission. + /// + /// The context of the application. + /// The identity group to check. + /// The permission to check for. + /// True if the identity group has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group, Type permission); + + /// + /// Checks if the specified identity role has the given permission. + /// + /// The type of the identity role. + /// The type of the identity permission. + /// The context of the application. + /// True if the identity role has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext) where R : IIdentityRole where P : IIdentityPermission; + + /// + /// Checks if the specified identity role has the given permission. + /// + /// The identity role to check. + /// The permission to check for. + /// True if the identity role has the permission, false otherwise. + bool CheckAccess(IApplicationContext applicationContext, Type role, Type permission); + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityPermission.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityPermission.cs new file mode 100644 index 0000000..67fe2c6 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityPermission.cs @@ -0,0 +1,12 @@ + +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Interface that defines an identity permission. + /// + public interface IIdentityPermission : IComponent + { + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs new file mode 100644 index 0000000..9ab9e73 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs @@ -0,0 +1,33 @@ +using System; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Defines the context for a permission, providing access to various related contexts and properties. + /// + public interface IIdentityPermissionContext : IContext + { + /// + /// Returns the permission id. + /// + string PermissionId { get; } + + /// + /// Returns the permission. + /// + Type Permission { get; } + + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityResource.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityResource.cs new file mode 100644 index 0000000..ce18045 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityResource.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Interface for identity resources. + /// + public interface IIdentityResource + { + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityRole.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityRole.cs new file mode 100644 index 0000000..b48dc12 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityRole.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Interface that defines an identity role. + /// + public interface IIdentityRole : IComponent + { + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs new file mode 100644 index 0000000..ac9657b --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs @@ -0,0 +1,27 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Defines the context for a role, providing access to various related contexts and properties. + /// + public interface IIdentityRoleContext : IContext + { + /// + /// Returns the role id. + /// + string RoleId { get; } + + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs new file mode 100644 index 0000000..37c8934 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs @@ -0,0 +1,584 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIdentity.Model; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSession.Model; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Management of identities (users). + /// + public class IdentityManager : IIdentityManager + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly IdentityPermissionDictionary _permissionDictionary = new(); + private readonly IdentityRoleDictionary _roleDictionary = new(); + + /// + /// Returns all permissions. + /// + public IEnumerable Permissions => _permissionDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.PermissionContext); + + /// + /// Returns all roles. + /// + public IEnumerable Roles => _roleDictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.RoleContext); + + /// + /// Returns all identities. + /// + public IEnumerable Identities => []; + + /// + /// Returns the current signed-in identity. + /// + public IIdentity CurrentIdentity { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The reference to the context of the host. + private IdentityManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:identitymanager.initialization" + ) + ); + } + + /// + /// Discovers and binds jobs to an application. + /// + /// The context of the plugin whose jobs are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_permissionDictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds jobs to an application. + /// + /// The context of the application whose jobs are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_permissionDictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers roles and ientities for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; + + // permissions + foreach (var permissionType in assembly.GetTypes().Where + ( + x => x.IsClass == true && + x.IsSealed && + x.IsPublic && + ( + x.GetInterface(typeof(IIdentityPermission).Name) != null + ) + )) + { + var id = permissionType.FullName?.ToLower(); + var roleTypes = new List(); + + foreach (var customAttribute in permissionType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IRoleAttribute)))) + { + if (customAttribute.AttributeType.Name == typeof(RoleAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(RoleAttribute<>).Namespace) + { + var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + if (type != null && !roleTypes.Contains(type)) + { + roleTypes.Add(type); + } + } + } + + // assign the event to existing applications + foreach (var applicationContext in applicationContexts) + { + var permissionContext = new IdentityPermissionContext() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + PermissionId = id, + Permission = permissionType + }; + + if (_permissionDictionary.AddPermissionItem + ( + pluginContext, + applicationContext, + new IdentityPermissionItem(_componentHub, _httpServerContext, pluginContext, applicationContext, permissionType, permissionContext, roleTypes) + )) + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:identitymanager.registerpermission", + id, + applicationContext.ApplicationId + ) + ); + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:identitymanager.duplicatepermission", + id, + applicationContext.ApplicationId + ) + ); + } + } + } + + // roles + foreach (var roleType in assembly.GetTypes().Where + ( + x => x.IsClass == true && + x.IsSealed && + x.IsPublic && + ( + x.GetInterface(typeof(IIdentityRole).Name) != null + ) + )) + { + var id = roleType.FullName?.ToLower(); + var permissionTypes = new List(); + + foreach (var customAttribute in roleType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IPermissionAttribute)))) + { + if (customAttribute.AttributeType.Name == typeof(PermissionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(PermissionAttribute<>).Namespace) + { + var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + if (type != null && !permissionTypes.Contains(type)) + { + permissionTypes.Add(type); + } + } + } + + // assign the event to existing applications + foreach (var applicationContext in applicationContexts) + { + var roleContext = new IdentityRoleContext() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + RoleId = id + }; + + if (_roleDictionary.AddRoleItem + ( + pluginContext, + applicationContext, + new IdentityRoleItem(_componentHub, _httpServerContext, pluginContext, applicationContext, roleType, roleContext, permissionTypes) + )) + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:identitymanager.registerrole", + id, + applicationContext.ApplicationId + ) + ); + } + else + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress:identitymanager.duplicaterole", + id, + applicationContext.ApplicationId + ) + ); + } + } + } + } + + /// + /// Removes all roles and permissions of an plugin. + /// + /// The context of the plugin that contains the identities to remove. + internal void Remove(IPluginContext pluginContext) + { + // permissions + if (_permissionDictionary.TryGetValue(pluginContext, out var permissionValue)) + { + foreach (var permissionItem in permissionValue + .SelectMany(x => x.Value)) + { + permissionItem.Dispose(); + } + + _permissionDictionary.Remove(pluginContext); + } + + // roles + if (_roleDictionary.TryGetValue(pluginContext, out var roleValue)) + { + foreach (var permissionItem in roleValue + .SelectMany(x => x.Value)) + { + permissionItem.Dispose(); + } + + _roleDictionary.Remove(pluginContext); + } + } + + /// + /// Removes all roles and permissions of an application. + /// + /// The context of the application that contains the identities to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + // permissions + foreach (var pluginDict in _permissionDictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var permissionItem in appDict) + { + permissionItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + + // roles + foreach (var pluginDict in _roleDictionary.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var roleItem in appDict) + { + roleItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + } + + /// + /// Raises the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Raises the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + + /// + /// Login an identity. + /// + /// The request. + /// The identity. + /// The password. + /// True if successful, false otherwise. + public bool Login(Request request, IIdentity identity, SecureString password) + { + if (identity?.PasswordHash == ComputeHash(password)) + { + var session = _componentHub.SessionManager.GetSession(request); + var authentification = session.GetOrCreateProperty(identity); + + if (authentification.Identity != identity) + { + return false; + } + + return true; + } + + return false; + } + + /// + /// Logout an identity. + /// + /// The request. + public void Logout(Request request) + { + var session = _componentHub.SessionManager.GetSession(request); + session.RemoveProperty(); + } + + /// + /// Returns the current signed-in identity based on the provided request. + /// + /// The request to get the current identity for. + /// The current signed-in identity. + public IIdentity GetCurrentIdentity(Request request) + { + var session = _componentHub.SessionManager.GetSession(request); + var authentification = session.GetProperty(); + + return authentification?.Identity; + } + + /// + /// Checks if the specified identity has the given permission. + /// + /// The type of the identity permission. + /// The context of the application. + /// The identity to check. + /// True if the identity has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext, IIdentity identity) where T : IIdentityPermission + { + return CheckAccess(applicationContext, identity, typeof(T)); + } + + /// + /// Checks if the specified identity has the given permission. + /// + /// The context of the application. + /// The identity to check. + /// The permission to check for. + /// True if the identity has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext, IIdentity identity, Type permission) + { + foreach (var group in identity?.Groups ?? []) + { + if (CheckAccess(applicationContext, group, permission)) + { + return true; + } + } + + return false; + } + + /// + /// Checks if the specified identity group has the given permission. + /// + /// The type of the identity permission. + /// The context of the application. + /// The identity group to check. + /// True if the identity group has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group) where T : IIdentityPermission + { + return CheckAccess(applicationContext, group, typeof(T)); + } + + /// + /// Checks if the specified identity group has the given permission. + /// + /// The identity group to check. + /// The context of the application. + /// The permission to check for. + /// True if the identity group has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group, Type permission) + { + foreach (var role in group?.Roles ?? []) + { + if (CheckAccess(applicationContext, role, permission)) + { + return true; + } + } + + return false; + } + + /// + /// Checks if the specified identity role has the given permission. + /// + /// The type of the identity role. + /// The type of the identity permission. + /// The context of the application. + /// True if the identity role has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext) where R : IIdentityRole where P : IIdentityPermission + { + return CheckAccess(applicationContext, typeof(R), typeof(P)); + } + /// + /// Checks if the specified identity role has the given permission. + /// + /// The context of the application. + /// The identity role to check. + /// The permission to check for. + /// True if the identity role has the permission, false otherwise. + public bool CheckAccess(IApplicationContext applicationContext, Type roleType, Type permissionType) + { + return CheckAccess(applicationContext, roleType.FullName.ToLower(), permissionType); + } + + /// + /// Checks if the specified identity role has the given permission. + /// + /// The context of the application. + /// The identity role to check. + /// The permission to check for. + /// True if the identity role has the permission, false otherwise. + private bool CheckAccess(IApplicationContext applicationContext, string roleName, Type permissionType) + { + // roles to permissions + var roles = _roleDictionary.Values.SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(entry => entry.Value); + + foreach (var role in roles.Where(x => x.RoleClass.FullName.Equals(roleName, StringComparison.CurrentCultureIgnoreCase))) + { + if (role.Permissions.Contains(permissionType)) + { + return true; + } + } + + // permissions to roles + var permissions = _permissionDictionary.Values.SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(entry => entry.Value); + + foreach (var permission in permissions.Where(x => x.PermissionClass == permissionType)) + { + if (permission.Roles.Any(x => x.FullName.Equals(roleName, StringComparison.CurrentCultureIgnoreCase))) + { + return true; + } + } + + return false; + } + + /// + /// Computes the SHA-256 hash of the input string. + /// + /// The input string to hash. + /// The computed hash as a hexadecimal string. + public static string ComputeHash(SecureString input) + { + var bstr = IntPtr.Zero; + try + { + bstr = Marshal.SecureStringToBSTR(input); + var length = Marshal.ReadInt32(bstr, -4); + var bytes = new byte[length]; + Marshal.Copy(bstr, bytes, 0, length); + var hashBytes = SHA256.HashData(bytes); + var builder = new StringBuilder(); + + foreach (var b in hashBytes) + { + builder.Append(b.ToString("x2")); + } + + return builder.ToString(); + } + finally + { + if (bstr != IntPtr.Zero) + { + Marshal.ZeroFreeBSTR(bstr); + } + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs b/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs new file mode 100644 index 0000000..43e6a70 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs @@ -0,0 +1,41 @@ +using System; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Defines the context for a permission, providing access to various related contexts and properties. + /// + public class IdentityPermissionContext : IIdentityPermissionContext + { + /// + /// Returns the permission id. + /// + public string PermissionId { get; internal set; } + + /// + /// Returns the permission. + /// + public Type Permission { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Permission: {PermissionId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs b/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs new file mode 100644 index 0000000..b380a67 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs @@ -0,0 +1,35 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity +{ + /// + /// Defines the context for a role, providing access to various related contexts and properties. + /// + public class IdentityRoleContext : IIdentityRoleContext + { + /// + /// Returns the role id. + /// + public string RoleId { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Role: {RoleId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityItem.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityItem.cs new file mode 100644 index 0000000..5ad80ca --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityItem.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebIdentity.Model +{ + /// + /// Represents an identity item within the WebExpress framework. + /// + public class IdentityItem + { + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs new file mode 100644 index 0000000..ec759c1 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity.Model +{ + /// + /// The identity permission directory. + /// + internal class IdentityPermissionDictionary : Dictionary>> + { + /// + /// Adds a permission item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The permission item. + /// True if the permission item was successfully added, false if an item with the same permission class already exists. + public bool AddPermissionItem(IPluginContext pluginContext, IApplicationContext applicationContext, IdentityPermissionItem permissionItem) + { + var type = permissionItem.PermissionClass; + + if (!typeof(IIdentityPermission).IsAssignableFrom(type)) + { + return false; + } + + if (!TryGetValue(pluginContext, out var appContextDict)) + { + appContextDict = new Dictionary>(); + this[pluginContext] = appContextDict; + } + + if (!appContextDict.TryGetValue(applicationContext, out var permissionList)) + { + permissionList = new List(); + appContextDict[applicationContext] = permissionList; + } + + if (permissionList.Any(x => x.PermissionClass == type)) + { + return false; // an item with the same permission class already exists + } + + permissionList.Add(permissionItem); + + return true; + } + + /// + /// Removes a permission item from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemovePermissionItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IIdentityPermission + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var permissionList = appContextDict[applicationContext]; + + var itemToRemove = permissionList.FirstOrDefault(x => x.PermissionClass == type); + if (itemToRemove != null) + { + permissionList.Remove(itemToRemove); + + if (permissionList.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the permission items from the dictionary. + /// + /// The type of the permission. + /// The application context. + /// An IEnumerable of permission items + public IEnumerable GetPermissionItems(IApplicationContext applicationContext) where T : IIdentityPermission + { + return GetPermissionItems(applicationContext, typeof(T)); + } + + /// + /// Returns the permission items from the dictionary. + /// + /// The application context. + /// The type of the permission. + /// An IEnumerable of permission items + public IEnumerable GetPermissionItems(IApplicationContext applicationContext, Type permissionType) + { + if (!typeof(IIdentityPermission).IsAssignableFrom(permissionType)) + { + return Enumerable.Empty(); + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var permissionList = appContextDict[applicationContext]; + + return permissionList.Where(x => x.PermissionClass == permissionType); + } + } + + return Enumerable.Empty(); + } + + /// + /// Returns all permission contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of permission contexts. + public IEnumerable GetPermissionContexts(IPluginContext pluginContext) + { + return this.Where(entry => entry.Key == pluginContext) + .SelectMany(entry => entry.Value.Keys); + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs new file mode 100644 index 0000000..51881f6 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity.Model +{ + /// + /// Represents an identity permission item used in the web identity system. + /// + public class IdentityPermissionItem + { + private readonly IComponentHub _componentHub; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; private set; } + + /// + /// Returns the roles associated with the permission. + /// + public IEnumerable Roles { get; private set; } + + /// + /// Returns or sets the permission context. + /// + public IIdentityPermissionContext PermissionContext { get; private set; } + + /// + /// Returns or sets the permission class. + /// + public Type PermissionClass { get; private set; } + + /// + /// Returns the permission instance. + /// + public IIdentityPermission Instance { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The associated component hub. + /// The reference to the context of the host. + /// The associated plugin context. + /// The corresponding application context. + /// The permission class. + /// The permission context. + /// The roles associated with the permission. + public IdentityPermissionItem(IComponentHub componentHub, IHttpServerContext httpServerContext, IPluginContext pluginContext, IApplicationContext applicationContext, Type permissionClass, IIdentityPermissionContext permissionContext, IEnumerable roles) + { + _componentHub = componentHub; + PluginContext = pluginContext; + ApplicationContext = applicationContext; + Roles = roles; + PermissionClass = permissionClass; + PermissionContext = permissionContext; + Instance = ComponentActivator.CreateInstance(httpServerContext, _componentHub, PermissionClass, PermissionContext); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + Instance?.Dispose(); + } + + /// + /// Convert the resource element to a string. + /// + /// The event element in its string representation. + public override string ToString() + { + return $"Permission: '{PermissionClass.FullName.ToLower()}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs new file mode 100644 index 0000000..48b55fe --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity.Model +{ + /// + /// The identity role directory. + /// + internal class IdentityRoleDictionary : Dictionary>> + { + /// + /// Adds a role item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The role item. + /// True if the role item was successfully added, false if an item with the same role class already exists. + public bool AddRoleItem(IPluginContext pluginContext, IApplicationContext applicationContext, IdentityRoleItem roleItem) + { + var type = roleItem.RoleClass; + + if (!typeof(IIdentityRole).IsAssignableFrom(type)) + { + return false; + } + + if (!TryGetValue(pluginContext, out var appContextDict)) + { + appContextDict = new Dictionary>(); + this[pluginContext] = appContextDict; + } + + if (!appContextDict.TryGetValue(applicationContext, out var roleList)) + { + roleList = new List(); + appContextDict[applicationContext] = roleList; + } + + if (roleList.Any(x => x.RoleClass == type)) + { + return false; // an item with the same role class already exists + } + + roleList.Add(roleItem); + + return true; + } + + /// + /// Removes a role item from the dictionary. + /// + /// The plugin context. + /// The application context. + public void RemoveRoleItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IIdentityRole + { + var type = typeof(T); + + if (ContainsKey(pluginContext)) + { + var appContextDict = this[pluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var roleList = appContextDict[applicationContext]; + + var itemToRemove = roleList.FirstOrDefault(x => x.RoleClass == type); + if (itemToRemove != null) + { + roleList.Remove(itemToRemove); + + if (roleList.Count == 0) + { + appContextDict.Remove(applicationContext); + + if (appContextDict.Count == 0) + { + Remove(pluginContext); + } + } + } + } + } + } + + /// + /// Returns the role items from the dictionary. + /// + /// The type of the role. + /// The application context. + /// An IEnumerable of role items + public IEnumerable GetRoleItems(IApplicationContext applicationContext) where T : IIdentityRole + { + return GetRoleItems(applicationContext, typeof(T)); + } + + /// + /// Returns the role items from the dictionary. + /// + /// The application context. + /// The type of the role. + /// An IEnumerable of role items + public IEnumerable GetRoleItems(IApplicationContext applicationContext, Type roleType) + { + if (!typeof(IIdentityRole).IsAssignableFrom(roleType)) + { + return Enumerable.Empty(); + } + + if (ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = this[applicationContext?.PluginContext]; + + if (appContextDict.ContainsKey(applicationContext)) + { + var roleList = appContextDict[applicationContext]; + + return roleList.Where(x => x.RoleClass == roleType); + } + } + + return Enumerable.Empty(); + } + + /// + /// Returns all role contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of role contexts. + public IEnumerable GetRoleContexts(IPluginContext pluginContext) + { + return this.Where(x => x.Key == pluginContext) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Select(x => x.RoleContext); + } + + /// + /// Returns all role contexts for a given application context. + /// + /// The application context. + /// An IEnumerable of role contexts. + public IEnumerable GetRoleContexts(IApplicationContext applicationContext) + { + return Values.SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(entry => entry.Value) + .Select(x => x.RoleContext); + } + } +} diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs new file mode 100644 index 0000000..6b9b2d4 --- /dev/null +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebIdentity.Model +{ + /// + /// Represents an item in the identity role. + /// + public class IdentityRoleItem + { + private readonly IComponentHub _componentHub; + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; private set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; private set; } + + /// + /// Returns or sets the role context. + /// + public IIdentityRoleContext RoleContext { get; private set; } + + /// + /// Returns the permissions associated with the role. + /// + public IEnumerable Permissions { get; private set; } + + /// + /// Returns or sets the role class. + /// + public Type RoleClass { get; private set; } + + /// + /// Returns the role instance. + /// + public IIdentityRole Instance { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The associated component hub. + /// The reference to the context of the host. + /// The associated plugin context. + /// The corresponding application context. + /// The role class. + /// The role context. + /// The permissions associated with the role. + public IdentityRoleItem(IComponentHub componentHub, IHttpServerContext httpServerContext, IPluginContext pluginContext, IApplicationContext applicationContext, Type permissionClass, IIdentityRoleContext roleContext, IEnumerable permissions) + { + _componentHub = componentHub; + PluginContext = pluginContext; + ApplicationContext = applicationContext; + Permissions = permissions; + RoleClass = permissionClass; + RoleContext = roleContext; + Instance = ComponentActivator.CreateInstance(httpServerContext, _componentHub, RoleClass, RoleContext); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + Instance?.Dispose(); + } + + /// + /// Convert the resource element to a string. + /// + /// The event element in its string representation. + public override string ToString() + { + return $"Role: '{RoleClass.FullName.ToLower()}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs index 888866d..47cfa86 100644 --- a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs @@ -70,7 +70,7 @@ public class RequestHeaderFields /// /// Returns the cookies. /// - public ICollection Cookies { get; } = new List(); + public ICollection Cookies { get; } = []; /// /// Returns the referer. The referer header echoes the absolute or partial address from diff --git a/src/WebExpress.WebCore/WebSession/Model/Session.cs b/src/WebExpress.WebCore/WebSession/Model/Session.cs index ad2e3f7..709ee9b 100644 --- a/src/WebExpress.WebCore/WebSession/Model/Session.cs +++ b/src/WebExpress.WebCore/WebSession/Model/Session.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; namespace WebExpress.WebCore.WebSession.Model { @@ -56,7 +58,7 @@ public Session(Guid id) /// /// The type of the property. /// The property or null. - public T GetProperty() where T : class, ISessionProperty, new() + public T GetProperty() where T : class, ISessionProperty { lock (Properties) { @@ -73,17 +75,47 @@ public Session(Guid id) /// Returns a property if it already exists. Otherwise, a new property will be created. /// /// The type of the property. - /// The property or null. - public T GetOrCreateProperty() where T : class, ISessionProperty, new() + /// The parameters to pass to the constructor of the property if it needs to be created. + /// The property or null if it cannot be created. + public T GetOrCreateProperty(params object[] parameters) where T : class, ISessionProperty { + var type = typeof(T); lock (Properties) { if (Properties.ContainsKey(typeof(T))) { - return Properties[typeof(T)] as T; + return Properties[type] as T; + } + + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = type.GetConstructors(flags); + + if (constructors != null || parameters.Length > 0) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var constructorParameters = constructor.GetParameters(); + var parameterValues = constructorParameters.Select + ( + x => parameters.Where + ( + y => y.GetType() == x.ParameterType || + x.ParameterType.IsAssignableFrom(y.GetType()) || + y.GetType().IsSubclassOf(x.ParameterType) + ).FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T injectionProperty) + { + SetProperty(injectionProperty); + + return injectionProperty; + } + } } - var property = new T(); + var property = Activator.CreateInstance(); SetProperty(property); return property; @@ -111,7 +143,7 @@ public void SetProperty(ISessionProperty property) /// Removes a property. /// /// The type of the property. - public void RemoveProperty() where T : class, ISessionProperty, new() + public void RemoveProperty() where T : class, ISessionProperty { lock (Properties) { diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs index ccb0252..2220435 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthentification.cs @@ -1,18 +1,27 @@ -namespace WebExpress.WebCore.WebSession.Model + +using WebExpress.WebCore.WebIdentity; + +namespace WebExpress.WebCore.WebSession.Model { /// /// Represents the authentication session property. + /// Authentication is the process of verifying the identity of a person or system to ensure that someone is who they claim to be. /// public class SessionPropertyAuthentification : SessionProperty { /// - /// Returns or sets the login name. + /// Returns the identity. /// - public string Identification { get; set; } + public IIdentity Identity { get; } /// - /// Provides or sets the password. + /// Initializes a new instance of the class. /// - public string Password { get; set; } + /// The identity to be set. + public SessionPropertyAuthentification(IIdentity identity) + { + Identity = identity; + } + } } diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs index af271b1..d5f9384 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyAuthorization.cs @@ -2,6 +2,7 @@ { /// /// Represents a session property authorization. + /// Authorization is the process of determining which resources and actions are allowed for an authenticated person or system. /// public class SessionPropertyAuthorization : SessionProperty { From 129630868d0bef81f832c126dc044431e9eabcb0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 25 Nov 2024 20:03:23 +0100 Subject: [PATCH 042/162] general optimizations --- .../WebMessage/RequestHeaderFields.cs | 14 +++++--- .../WebMessage/ResponseHeaderFields.cs | 34 +++++++++---------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs index 47cfa86..7c1f998 100644 --- a/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/RequestHeaderFields.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebMessage { /// - /// see RFC 2616 + /// Represents the header fields of an HTTP request as defined in RFC 2616. /// public class RequestHeaderFields { @@ -50,7 +50,7 @@ public class RequestHeaderFields /// /// Returns the accepted media types. /// - public ICollection Accept { get; private set; } + public IEnumerable Accept { get; private set; } /// /// Returns the accepted encodings. @@ -70,7 +70,7 @@ public class RequestHeaderFields /// /// Returns the cookies. /// - public ICollection Cookies { get; } = []; + public IEnumerable Cookies { get; } = []; /// /// Returns the referer. The referer header echoes the absolute or partial address from @@ -92,22 +92,26 @@ internal RequestHeaderFields(IFeatureCollection contextFeatures) ContentType = requestFeature.Headers.ContentType; ContentLength = requestFeature.Headers.ContentLength ?? 0; ContentLanguage = requestFeature.Headers.ContentLanguage; - ContentEncoding = requestFeature.Headers.ContentEncoding.Any() ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; + ContentEncoding = requestFeature.Headers.ContentEncoding.Count != 0 ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; Accept = requestFeature.Headers.Accept; AcceptEncoding = requestFeature.Headers.AcceptEncoding; AcceptLanguage = requestFeature.Headers.AcceptLanguage.SelectMany(x => x.Split(';', StringSplitOptions.RemoveEmptyEntries)); UserAgent = requestFeature.Headers.UserAgent; Referer = requestFeature.Headers.Referer; + var cookies = new List(); + foreach (var cookie in requestFeature.Headers.Cookie) { var split = cookie.Split('='); var key = split[0]; var value = split[1]; - Cookies.Add(new Cookie(key, value)); + cookies.Add(new Cookie(key, value)); } + Cookies = cookies; + Authorization = RequestAuthorization.Parse(requestFeature.Headers.Authorization); } } diff --git a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs index 25420f6..303ff8f 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseHeaderFields.cs @@ -5,54 +5,54 @@ namespace WebExpress.WebCore.WebMessage { /// - /// see RFC 2616 + /// Represents the response header fields as per RFC 2616. /// public class ResponseHeaderFields { /// - /// Liefert oder setzt die Content-Länge + /// Returns or sets the content length. /// public int ContentLength { get; set; } /// - /// Liefert oder setzt den Content-Typ + /// Returns or sets the content type. /// public string ContentType { get; set; } /// - /// Liefert oder setzt die Sprache des Content + /// Returns or sets the content language. /// public string ContentLanguage { get; set; } /// - /// Liefert oder setzt die Direktiven für das Caching (see RFC 7234) + /// Returns or sets the cache control directives (see RFC 7234). /// public string CacheControl { get; set; } /// - /// ContentDisposition + /// Returns or sets the content disposition. /// public string ContentDisposition { get; set; } /// - /// Die Basic Authentication (Basisauthentifizierung) nach RFC 2617 + /// Returns or sets a value indicating whether basic authentication (as per RFC 2617) is required. /// public bool WWWAuthenticate { get; set; } /// - /// Location + /// Returns or sets the location. /// public string Location { get; set; } /// - /// Benutzerdefinierte Header + /// Returns the custom headers. /// - public Dictionary CustomHeader { get; private set; } + public IDictionary CustomHeader { get; private set; } /// - /// Liefert oder setzt die Cookies + /// Returns the cookies. /// - public CookieCollection Cookies { get; } = new CookieCollection(); + public CookieCollection Cookies { get; } = []; /// /// Initializes a new instance of the class. @@ -65,10 +65,10 @@ public ResponseHeaderFields() } /// - /// Setzt ein benutzerdefinierten Header + /// Adds a custom header. /// - /// - /// + /// The header key. + /// The header value. public void AddCustomHeader(string key, string value) { if (!CustomHeader.ContainsKey(key)) @@ -82,9 +82,9 @@ public void AddCustomHeader(string key, string value) } /// - /// In Stringform umwandeln + /// Converts the response header fields to a string representation. /// - /// + /// A string representation of the response header fields. public override string ToString() { var sb = new StringBuilder(); From 54cf88b60f81692177d42e91a975a541c2510145 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 25 Nov 2024 20:08:58 +0100 Subject: [PATCH 043/162] update to .net 9.0 #6 --- src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj | 2 +- src/WebExpress.WebCore/WebExpress.WebCore.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index 9cc3f69..a69126f 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -1,7 +1,7 @@  - net8.0 + net9.0 enable disable diff --git a/src/WebExpress.WebCore/WebExpress.WebCore.csproj b/src/WebExpress.WebCore/WebExpress.WebCore.csproj index b87036d..20d3a02 100644 --- a/src/WebExpress.WebCore/WebExpress.WebCore.csproj +++ b/src/WebExpress.WebCore/WebExpress.WebCore.csproj @@ -5,7 +5,7 @@ WebExpress.WebCore 0.0.8.0 0.0.8.0 - net8.0 + net9.0 any https://github.com/ReneSchwarzer/WebExpress.git Rene_Schwarzer@hotmail.de From 8129f491a320a5f8bed2165ade598b027d068759 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 25 Nov 2024 20:36:39 +0100 Subject: [PATCH 044/162] general optimizations in internationalization --- src/WebExpress.WebCore/HttpServer.cs | 37 +++++++---------- .../Internationalization/I18N.cs | 23 ----------- .../Internationalization/II18N.cs | 12 ------ .../IInternationalizationManager.cs | 17 -------- .../InternationalizationExtensions.cs | 40 ++----------------- .../InternationalizationManager.cs | 23 ----------- .../Internationalization/de | 6 +++ .../Internationalization/en | 6 +++ .../WebAsset/AssetManager.cs | 10 +---- src/WebExpress.WebCore/WebPage/PageManager.cs | 11 +---- .../WebResource/ResourceManager.cs | 11 +---- .../WebRestAPI/RestApiManager.cs | 11 +---- .../WebSettingPage/SettingPageManager.cs | 13 ++---- 13 files changed, 40 insertions(+), 180 deletions(-) delete mode 100644 src/WebExpress.WebCore/Internationalization/II18N.cs diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index de1fce8..7ca931f 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -29,7 +29,7 @@ namespace WebExpress.WebCore /// /// The web server for processing http requests (see RFC 2616). The web server uses Kestrel internally. /// - public class HttpServer : IHost, II18N, IHttpApplication + public class HttpServer : IHost, IHttpApplication { /// /// Event is triggered after the web server is started. @@ -101,12 +101,12 @@ public void Start() { if (HttpServerContext != null && HttpServerContext.Log != null) { - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.run")); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.run")); } if (!HttpListener.IsSupported) { - HttpServerContext.Log.Error(message: this.I18N("webexpress:httpserver.notsupported")); + HttpServerContext.Log.Error(message: I18N.Translate("webexpress:httpserver.notsupported")); } var logger = new LogFactory(); @@ -146,7 +146,7 @@ public void Start() Kestrel.StartAsync(this, ServerToken); - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.start"), args: [ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString()]); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.start"), args: [ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString()]); Started?.Invoke(this, new EventArgs()); } @@ -169,7 +169,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, End .Union(asterisk ? Dns.GetHostEntry("localhost").AddressList : []) .Where(x => x.AddressFamily == AddressFamily.InterNetwork || x.AddressFamily == AddressFamily.InterNetworkV6); - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.endpoint"), args: endPoint.Uri); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.endpoint"), args: endPoint.Uri); foreach (var ipAddress in addressList) { @@ -185,7 +185,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, End } catch (Exception ex) { - HttpServerContext.Log.Error(message: this.I18N("webexpress:httpserver.listen.exeption"), args: endPoint); + HttpServerContext.Log.Error(message: I18N.Translate("webexpress:httpserver.listen.exeption"), args: endPoint); HttpServerContext.Log.Exception(ex); } @@ -200,26 +200,26 @@ private void AddEndpoint(OptionsWrapper serverOptions, IPE { serverOptions.Value.Listen(endPoint); - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.listen"), args: endPoint.ToString()); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.listen"), args: endPoint.ToString()); } /// - /// Adds an endpoint. + /// Adds an endpoint with HTTPS configuration. /// /// The server options. - /// The certificate. - /// The password to the certificate. /// The endpoint. + /// The path to the PFX file containing the certificate. + /// The password for the PFX file. private void AddEndpoint(OptionsWrapper serverOptions, IPEndPoint endPoint, string pfxFile, string password) { serverOptions.Value.Listen(endPoint, configure => { - var cert = new X509Certificate2(pfxFile, password); + var cert = X509CertificateLoader.LoadPkcs12FromFile(pfxFile, password, X509KeyStorageFlags.DefaultKeySet); configure.UseHttps(cert); }); - HttpServerContext.Log.Info(message: this.I18N("webexpress:httpserver.listen"), args: endPoint.ToString()); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.listen"), args: endPoint.ToString()); } /// @@ -245,7 +245,7 @@ private Response HandleClient(HttpContext context) var culture = request.Culture; var uri = request?.Uri; - HttpServerContext.Log.Debug(message: this.I18N("webexpress:httpserver.connected"), args: context.RemoteEndPoint); + HttpServerContext.Log.Debug(message: I18N.Translate("webexpress:httpserver.connected"), args: context.RemoteEndPoint); HttpServerContext.Log.Info(I18N.Translate ( "webexpress:httpserver.request", @@ -443,18 +443,9 @@ private async Task SendResponseAsync(HttpContext context, Response response) /// The request. /// The plugin by searching the status page or null. /// The response. - private Response CreateStatusPage(string message, Request request, SearchResult searchResult = null) where T : Response, new() + private static Response CreateStatusPage(string message, Request request, SearchResult searchResult = null) where T : Response, new() { var response = new T() as Response; - var culture = Culture; - - try - { - culture = new CultureInfo(request?.Header?.AcceptLanguage?.FirstOrDefault()?.ToLower()); - } - catch - { - } if (searchResult != null) { diff --git a/src/WebExpress.WebCore/Internationalization/I18N.cs b/src/WebExpress.WebCore/Internationalization/I18N.cs index f345d84..a2c2c2a 100644 --- a/src/WebExpress.WebCore/Internationalization/I18N.cs +++ b/src/WebExpress.WebCore/Internationalization/I18N.cs @@ -29,29 +29,6 @@ public static string Translate(string key, params object[] args) return WebEx.ComponentHub?.InternationalizationManager?.Translate(key, args) ?? key; } - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The value of the key in the current language. - public static string Translate(II18N obj, string key) - { - return WebEx.ComponentHub?.InternationalizationManager?.Translate(obj, key) ?? key; - } - - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The formatting arguments. - /// The value of the key in the current language. - public static string Translate(II18N obj, string key, params object[] args) - { - return WebEx.ComponentHub?.InternationalizationManager?.Translate(obj, key, args) ?? key; - } - /// /// Translates a given key to the specified language. /// diff --git a/src/WebExpress.WebCore/Internationalization/II18N.cs b/src/WebExpress.WebCore/Internationalization/II18N.cs deleted file mode 100644 index 1fbfa61..0000000 --- a/src/WebExpress.WebCore/Internationalization/II18N.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Globalization; - -namespace WebExpress.WebCore.Internationalization -{ - public interface II18N - { - /// - /// Returns or sets the culture. - /// - CultureInfo Culture { get; set; } - } -} diff --git a/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs index 955b069..cfed473 100644 --- a/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/IInternationalizationManager.cs @@ -24,23 +24,6 @@ public interface IInternationalizationManager : IComponentManager /// The value of the key in the current language. public string Translate(string key, params object[] args); - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The value of the key in the current language. - string Translate(II18N obj, string key); - - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The formatting arguments. - /// The value of the key in the current language. - string Translate(II18N obj, string key, params object[] args); - /// /// Translates a given key to the specified language. /// diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 42b83fe..48e89ae 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -3,43 +3,11 @@ namespace WebExpress.WebCore.Internationalization { + /// + /// Provides extension methods for internationalization. + /// public static class InternationalizationExtensions { - /// - /// Internationalization of a key. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this II18N obj, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj, key); - } - - /// - /// Internationalization of a key. - /// - /// An internationalization object that is being extended. - /// The plugin id. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this II18N obj, string pluginId, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, pluginId, key); - } - - /// - /// Internationalization of a key. - /// - /// An internationalization object that is being extended. - /// The allication context. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this II18N obj, IApplicationContext applicationContext, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); - } - /// /// Internationalization of a key. /// @@ -67,7 +35,7 @@ public static string I18N(this RenderContext obj, string key) /// Internationalization of a key. /// /// An render context object that is being extended. - /// The allication context. + /// The application context. /// The internationalization key. /// The value of the key in the current language. public static string I18N(this RenderContext obj, IApplicationContext applicationContext, string key) diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 36a797e..4916a95 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -176,29 +176,6 @@ public string Translate(string key, params object[] args) return string.Format(Translate(DefaultCulture, null, key), args); } - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The value of the key in the current language. - public string Translate(II18N obj, string key) - { - return Translate(obj.Culture, key); - } - - /// - /// Translates a given key to the specified language. - /// - /// An internationalization object that is being extended. - /// The internationalization key. - /// The formatting arguments. - /// The value of the key in the current language. - public string Translate(II18N obj, string key, params object[] args) - { - return string.Format(Translate(obj, key), args); - } - /// /// Translates a given key to the specified language. /// diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 5eeb18d..4a43fc6 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -133,6 +133,12 @@ jobmanager.job.process=Der Job '{0}' wird ausgeführt. jobmanager.cron.parseerror=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' kann nicht verarbeitet werden. jobmanager.cron.range=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' ist außerhalb des gültigen Bereiches. +identitymanager.initialization=Der Identitymanager wurde initialisiert. +identitymanager.registerpermission=Die Berechtigung '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. +identitymanager.duplicatepermission=Die Berechtigung '{0}' wurde bereits in der Anwendung '{1}' registriert. +identitymanager.registerrole=Die Rolle '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. +identitymanager.duplicaterole=Die Rolle '{0}' wurde bereits in der Anwendung '{1}' registriert. + resource.variable.duplicate=Variable '{0}' bereits vorhanden! resource.file={0}: Datei '{1}' wurde geladen. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 8d3b49e..a587386 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -133,5 +133,11 @@ jobmanager.job.process=The job '{0}' is executed. jobmanager.cron.parseerror=Syntax error in the timing of a job. The value '{0}' cannot be processed. jobmanager.cron.range=Syntax error in the timing of a job. The value '{0}' is outside the valid range. +identitymanager.initialization=The identity manager has been initialized. +identitymanager.registerpermission=The permission '{0}' has been assigned to the application '{1}' and registered in the identity manager. +identitymanager.duplicatepermission=The permission '{0}' has already been registered in the application '{1}'. +identitymanager.registerrole=The role '{0}' has been assigned to the application '{1}' and registered in the identity manager. +identitymanager.duplicaterole=The role '{0}' has already been registered in the application '{1}'. + resource.variable.duplicate=Variable '{0}' already exists! resource.file={0}: File '{1}' has been loaded. \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 6a715e4..5ee8e5f 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -281,9 +280,8 @@ public IEnumerable GetAssets(IApplicationContext applicationConte /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. /// /// The context used for asset creation. - /// The culture with the language settings. /// The created or cached resource. - private IAsset CreateAssetInstance(IAssetContext assetContext, CultureInfo culture) + private IAsset CreateAssetInstance(IAssetContext assetContext) { var resourceItem = _itemDictionary.Values .SelectMany(x => x.Values) @@ -293,12 +291,6 @@ private IAsset CreateAssetInstance(IAssetContext assetContext, CultureInfo cultu if (resourceItem != null && resourceItem.Instance == null) { var instance = ComponentActivator.CreateInstance(resourceItem.AssetClass, assetContext, _httpServerContext, _componentHub); - - if (instance is II18N i18n) - { - i18n.Culture = culture; - } - resourceItem.Instance = instance; return instance; diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 4e71f9c..b1f196c 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -63,7 +62,7 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon EndpointsResolver = () => Pages, HandleRequest = (request, endpontContext) => { - var page = CreatePageInstance(endpontContext as IPageContext, request.Culture); + var page = CreatePageInstance(endpontContext as IPageContext); var pageType = page.GetType(); var context = default(IRenderContext); var pageContetx = endpontContext as IPageContext; @@ -213,9 +212,8 @@ public IPageContext GetPage(string applicationId, string pageId) /// Creates a new page and returns it. If a page already exists (through caching), the existing instance is returned. /// /// The context used for page creation. - /// The culture with the language settings. /// The created or cached page. - private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) + private IPage CreatePageInstance(IPageContext pageContext) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) @@ -226,11 +224,6 @@ private IPage CreatePageInstance(IPageContext pageContext, CultureInfo culture) { var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _httpServerContext, _componentHub); - if (instance is II18N i18n) - { - i18n.Culture = culture; - } - if (resourceItem.Cache) { resourceItem.Instance = instance; diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index b9251d4..8a4b6cb 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -63,7 +62,7 @@ private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServe HandleRequest = (request, endpointContext) => { var resourceContext = endpointContext as IResourceContext; - var resource = CreateResourceInstance(resourceContext, request.Culture); + var resource = CreateResourceInstance(resourceContext); return resource.Process(request); } @@ -381,9 +380,8 @@ public IResourceContext GetResorce(string applicationId, string resourceId) /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. /// /// The context used for resource creation. - /// The culture with the language settings. /// The created or cached resource. - private IResource CreateResourceInstance(IResourceContext resourceContext, CultureInfo culture) + private IResource CreateResourceInstance(IResourceContext resourceContext) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) @@ -394,11 +392,6 @@ private IResource CreateResourceInstance(IResourceContext resourceContext, Cultu { var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _httpServerContext, _componentHub); - if (instance is II18N i18n) - { - i18n.Culture = culture; - } - if (resourceItem.Cache) { resourceItem.Instance = instance; diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 14f3efa..6e4dccf 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Text; using System.Text.Json; @@ -66,7 +65,7 @@ private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServer HandleRequest = (request, endpointContext) => { var restApiContext = endpointContext as IRestApiContext; - var restApi = CreatePageInstance(restApiContext, request.Culture) as IRestApi; + var restApi = CreatePageInstance(restApiContext) as IRestApi; if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) { @@ -232,9 +231,8 @@ public IRestApiContext GetRestApi(string applicationId, string restApiId) /// Creates a new rest api resource and returns it. If a rest api resource already exists (through caching), the existing instance is returned. /// /// The context used for rest api resource creation. - /// The culture with the language settings. /// The created or cached rest api resource. - private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo culture) + private IRestApi CreatePageInstance(IRestApiContext pageContext) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) @@ -245,11 +243,6 @@ private IRestApi CreatePageInstance(IRestApiContext pageContext, CultureInfo cul { var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _httpServerContext, _componentHub); - if (instance is II18N i18n) - { - i18n.Culture = culture; - } - if (resourceItem.Cache) { resourceItem.Instance = instance; diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 97434ce..b3376d0 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -60,7 +59,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe EndpointsResolver = () => SettingPages, HandleRequest = (request, endpontContext) => { - var settingPage = CreateSettingPageInstance(endpontContext as ISettingPageContext, request.Culture); + var settingPage = CreateSettingPageInstance(endpontContext as ISettingPageContext); var settingPageType = settingPage.GetType(); var context = default(IRenderContext); var pageContetx = endpontContext as IPageContext; @@ -98,9 +97,8 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe /// Creates a new setting page and returns it. If a page already exists (through caching), the existing instance is returned. /// /// The context used for setting page creation. - /// The culture with the language settings. /// The created or cached page. - private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageContext, CultureInfo culture) + private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageContext) { var settingPageItem = _dictionary.Values .SelectMany(a => a.Values) @@ -114,11 +112,6 @@ private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageCon { var instance = ComponentActivator.CreateInstance(settingPageItem.SettingPageClass, settinPageContext, _httpServerContext, _componentHub); - if (instance is II18N i18n) - { - i18n.Culture = culture; - } - if (settingPageItem.Cache) { settingPageItem.Instance = instance; @@ -212,7 +205,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); } else if (customAttribute.AttributeType == typeof(SettingHideAttribute)) { From b9affaae22d4d32379249225ccd672302764533e Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 1 Dec 2024 18:23:38 +0100 Subject: [PATCH 045/162] add fragment manager - migrated fragmentmanager from webexpress.ui to webexpress.core #7 --- .../Fixture/UnitTestControlFixture.cs | 493 ++++++++--------- .../Manager/UnitTestFragmentManager.cs | 104 ++++ .../Request/UnitTestRequest.cs | 16 - src/WebExpress.WebCore.Test/TestFragmentA.cs | 55 ++ src/WebExpress.WebCore.Test/TestFragmentB.cs | 54 ++ src/WebExpress.WebCore.Test/TestPageB.cs | 3 +- src/WebExpress.WebCore.Test/TestScopeA.cs | 11 + src/WebExpress.WebCore.Test/TestScopeB.cs | 11 + src/WebExpress.WebCore.Test/TestSectionA.cs | 11 + .../InternationalizationExtensions.cs | 2 +- .../Internationalization/de | 9 + .../Internationalization/en | 9 + .../WebAttribute/IFragmentAttribute.cs | 9 + .../WebAttribute/OrderAttribute.cs | 17 + .../WebAttribute/ScopeAttribute.cs | 2 +- .../WebAttribute/SectionAttribute.cs | 19 + .../WebComponent/ComponentHub.cs | 10 + .../WebComponent/IComponentHub.cs | 7 + .../WebFragment/FragmentComparer.cs | 40 ++ .../WebFragment/FragmentContext.cs | 57 ++ .../WebFragment/FragmentManager.cs | 501 ++++++++++++++++++ .../WebFragment/IFragment.cs | 17 + .../WebFragment/IFragmentContext.cs | 51 ++ .../WebFragment/IFragmentDynamic.cs | 27 + .../WebFragment/IFragmentManager.cs | 95 ++++ .../WebFragment/Model/FragmentDictionary.cs | 157 ++++++ .../WebFragment/Model/FragmentItem.cs | 125 +++++ .../WebPage/IPageContext.cs | 5 +- .../WebPage/IRenderContext.cs | 13 +- .../WebPage/Model/PageItem.cs | 2 +- src/WebExpress.WebCore/WebPage/PageContext.cs | 2 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 8 +- .../WebPage/RenderContext.cs | 23 +- src/WebExpress.WebCore/WebSection/ISection.cs | 9 + .../WebSettingPage/SettingPageManager.cs | 2 +- 35 files changed, 1677 insertions(+), 299 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs delete mode 100644 src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs create mode 100644 src/WebExpress.WebCore.Test/TestFragmentA.cs create mode 100644 src/WebExpress.WebCore.Test/TestFragmentB.cs create mode 100644 src/WebExpress.WebCore.Test/TestScopeA.cs create mode 100644 src/WebExpress.WebCore.Test/TestScopeB.cs create mode 100644 src/WebExpress.WebCore.Test/TestSectionA.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IFragmentAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs create mode 100644 src/WebExpress.WebCore/WebFragment/FragmentComparer.cs create mode 100644 src/WebExpress.WebCore/WebFragment/FragmentContext.cs create mode 100644 src/WebExpress.WebCore/WebFragment/FragmentManager.cs create mode 100644 src/WebExpress.WebCore/WebFragment/IFragment.cs create mode 100644 src/WebExpress.WebCore/WebFragment/IFragmentContext.cs create mode 100644 src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs create mode 100644 src/WebExpress.WebCore/WebFragment/IFragmentManager.cs create mode 100644 src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs create mode 100644 src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs create mode 100644 src/WebExpress.WebCore/WebSection/ISection.cs diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index d727d60..adb9d43 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -1,245 +1,248 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using System.Globalization; -using System.Net; -using System.Reflection; -using System.Text; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Fixture -{ - /// - /// A fixture class for unit tests, providing various mock objects and utility methods. - /// - public class UnitTestControlFixture : IDisposable - { - private static readonly string[] _separator = ["\r\n", "\r", "\n"]; - - /// - /// Returns the list of resources. - /// - private static List Ressources { get; } = []; - - - /// - /// Initializes a new instance of the class and boot the component manager. - /// - public UnitTestControlFixture() - { - } - - /// - /// Create a fake server context. - /// - /// The server context. - public static IHttpServerContext CreateHttpServerContextMock() - { - return new HttpServerContext - ( - "localhost", - [], - "", - Environment.CurrentDirectory, - Environment.CurrentDirectory, - Environment.CurrentDirectory, - null, - CultureInfo.GetCultureInfo("en"), - new Log() { LogMode = LogMode.Off }, - null - ); - } - - /// - /// Create a component hub. - /// - /// The component hub. - public static ComponentHub CreateComponentHubMock() - { - var ctorComponentManager = typeof(ComponentHub).GetConstructor - ( - BindingFlags.NonPublic | BindingFlags.Instance, - null, - [typeof(HttpServerContext)], - null - ); - - var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContextMock()]); - - // set static field in the webex class - var type = typeof(WebEx); - var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); - - field.SetValue(null, componentManager); - - return componentManager; - } - - /// - /// Create a component hub and register the plugins. - /// - /// The component hub. - public static ComponentHub CreateAndRegisterComponentHubMock() - { - var componentManager = CreateComponentHubMock(); - var pluginManager = componentManager.PluginManager as PluginManager; - - pluginManager.Register(); - - return componentManager; - } - - /// - /// Create a fake request. - /// - /// The content of the request. - /// The URI of the request. - /// A fake request for testing. - public static WebMessage.Request CrerateRequestMock(string content = "", string uri = "") - { - var context = CreateHttpContextMock(content); - - var request = context.Request; - - if (!string.IsNullOrEmpty(uri)) - { - request.Uri = new UriResource(uri); - } - - return request; - } - - /// - /// Create a fake http context. - /// - /// The content. - /// A fake http context for testing. - public static WebMessage.HttpContext CreateHttpContextMock(string content = "") - { - var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); - var featureCollection = new FeatureCollection(); - var firstLine = content.Split('\n').FirstOrDefault(); - var lines = content.Split(_separator, StringSplitOptions.None); - var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); - var pos = content.Length > 0 ? content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4 : 0; - var innerContent = pos < content.Length ? content[pos..] : ""; - var contentBytes = Encoding.UTF8.GetBytes(innerContent); - - var requestFeature = new HttpRequestFeature - { - Headers = new HeaderDictionary - { - ["Host"] = "localhost", - ["Connection"] = "keep-alive", - ["ContentType"] = "text/html", - ["ContentLength"] = innerContent.Length.ToString(), - ["ContentLanguage"] = "en", - ["ContentEncoding"] = "gzip, deflate, br, zstd", - ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", - ["AcceptEncoding"] = "gzip, deflate, br, zstd", - ["AcceptLanguage"] = "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", - ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", - ["Referer"] = "0HN50661TV8TP", - ["Cookie"] = "session=AB333C76-E73F-45E0-85FD-123320D9B85F" - }, - Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, - Method = firstLine.Split(' ')?.Where(x => !string.IsNullOrEmpty(x)).FirstOrDefault() ?? "GET", - RawTarget = firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.FirstOrDefault() ?? "/", - QueryString = "?" + firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.Skip(1)?.FirstOrDefault() ?? "", - }; - - foreach (var line in filteredLines) - { - var key = line.Split(':').FirstOrDefault().Trim(); - var value = line.Split(':').Skip(1).FirstOrDefault().Trim(); - requestFeature.Headers[key] = value; - } - - requestFeature.Headers.ContentLength = contentBytes.Length; - - var requestIdentifierFeature = new HttpRequestIdentifierFeature - { - TraceIdentifier = "Ihr TraceIdentifier-Wert" - }; - - var connectionFeature = new HttpConnectionFeature - { - LocalPort = 8080, - LocalIpAddress = IPAddress.Parse("192.168.0.1"), - RemotePort = 8080, - RemoteIpAddress = IPAddress.Parse("127.0.0.1"), - ConnectionId = "0HN50661TV8TP" - }; - - featureCollection.Set(requestFeature); - featureCollection.Set(requestIdentifierFeature); - featureCollection.Set(connectionFeature); - - var componentManager = CreateComponentHubMock(); - var context = new WebMessage.HttpContext(featureCollection, CreateHttpServerContextMock()); - - return context; - } - - /// - /// Create a fake render context. - /// - /// A fake context for testing. - public static RenderContext CrerateContextMock() - { - var request = CrerateRequestMock(); - - return new RenderContext(CreratePageContextMock()?.ApplicationContext, request, []); - } - - /// - /// Create a fake page context. - /// - /// A fake context for testing. - public static PageContext CreratePageContextMock() - { - var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IApplicationContext)], null); - - var applicationContext = WebEx.ComponentHub.ApplicationManager.Applications - .Where(x => x.ApplicationId.Equals(typeof(TestApplicationA).FullName, StringComparison.CurrentCultureIgnoreCase)) - .FirstOrDefault(); - - var pageContext = (PageContext)ctorPageContext.Invoke([applicationContext]); - - return pageContext; - } - - /// - /// Gets the content of an embedded resource as a string. - /// - /// The name of the resource file. - /// The content of the embedded resource as a string. - public static string GetEmbeddedResource(string fileName) - { - var assembly = typeof(UnitTestControlFixture).Assembly; - var resourceName = assembly.GetManifestResourceNames() - .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); - - using var stream = assembly.GetManifestResourceStream(resourceName); - using var memoryStream = new MemoryStream(); - stream.CopyTo(memoryStream); - var data = memoryStream.ToArray(); - - return Encoding.UTF8.GetString(data); - } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - GC.SuppressFinalize(this); - } - } -} +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using System.Globalization; +using System.Net; +using System.Reflection; +using System.Text; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebResource; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.Test.Fixture +{ + /// + /// A fixture class for unit tests, providing various mock objects and utility methods. + /// + public class UnitTestControlFixture : IDisposable + { + private static readonly string[] _separator = ["\r\n", "\r", "\n"]; + + /// + /// Returns the list of resources. + /// + private static List Ressources { get; } = []; + + + /// + /// Initializes a new instance of the class and boot the component manager. + /// + public UnitTestControlFixture() + { + } + + /// + /// Create a fake server context. + /// + /// The server context. + public static IHttpServerContext CreateHttpServerContextMock() + { + return new HttpServerContext + ( + "localhost", + [], + "", + Environment.CurrentDirectory, + Environment.CurrentDirectory, + Environment.CurrentDirectory, + null, + CultureInfo.GetCultureInfo("en"), + new Log() { LogMode = LogMode.Off }, + null + ); + } + + /// + /// Create a component hub. + /// + /// The component hub. + public static ComponentHub CreateComponentHubMock() + { + var ctorComponentManager = typeof(ComponentHub).GetConstructor + ( + BindingFlags.NonPublic | BindingFlags.Instance, + null, + [typeof(HttpServerContext)], + null + ); + + var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContextMock()]); + + // set static field in the webex class + var type = typeof(WebEx); + var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); + + field.SetValue(null, componentManager); + + return componentManager; + } + + /// + /// Create a component hub and register the plugins. + /// + /// The component hub. + public static ComponentHub CreateAndRegisterComponentHubMock() + { + var componentManager = CreateComponentHubMock(); + var pluginManager = componentManager.PluginManager as PluginManager; + + pluginManager.Register(); + + return componentManager; + } + + /// + /// Create a fake request. + /// + /// The content of the request. + /// The URI of the request. + /// A fake request for testing. + public static WebMessage.Request CrerateRequestMock(string content = "", string uri = "") + { + var context = CreateHttpContextMock(content); + + var request = context.Request; + + if (!string.IsNullOrEmpty(uri)) + { + request.Uri = new UriResource(uri); + } + + return request; + } + + /// + /// Create a fake http context. + /// + /// The content. + /// A fake http context for testing. + public static WebMessage.HttpContext CreateHttpContextMock(string content = "") + { + var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); + var featureCollection = new FeatureCollection(); + var firstLine = content.Split('\n').FirstOrDefault(); + var lines = content.Split(_separator, StringSplitOptions.None); + var filteredLines = lines.Skip(1).TakeWhile(line => !string.IsNullOrWhiteSpace(line)); + var pos = content.Length > 0 ? content.IndexOf(filteredLines.LastOrDefault()) + filteredLines.LastOrDefault().Length + 4 : 0; + var innerContent = pos < content.Length ? content[pos..] : ""; + var contentBytes = Encoding.UTF8.GetBytes(innerContent); + + var requestFeature = new HttpRequestFeature + { + Headers = new HeaderDictionary + { + ["Host"] = "localhost", + ["Connection"] = "keep-alive", + ["ContentType"] = "text/html", + ["ContentLength"] = innerContent.Length.ToString(), + ["ContentLanguage"] = "en", + ["ContentEncoding"] = "gzip, deflate, br, zstd", + ["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + ["AcceptEncoding"] = "gzip, deflate, br, zstd", + ["AcceptLanguage"] = "de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6", + ["UserAgent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", + ["Referer"] = "0HN50661TV8TP", + ["Cookie"] = "session=AB333C76-E73F-45E0-85FD-123320D9B85F" + }, + Body = contentBytes.Length > 0 ? new MemoryStream(contentBytes) : null, + Method = firstLine.Split(' ')?.Where(x => !string.IsNullOrEmpty(x)).FirstOrDefault() ?? "GET", + RawTarget = firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.FirstOrDefault() ?? "/", + QueryString = "?" + firstLine.Split(' ')?.Skip(1)?.FirstOrDefault()?.Split('?')?.Skip(1)?.FirstOrDefault() ?? "", + }; + + foreach (var line in filteredLines) + { + var key = line.Split(':').FirstOrDefault().Trim(); + var value = line.Split(':').Skip(1).FirstOrDefault().Trim(); + requestFeature.Headers[key] = value; + } + + requestFeature.Headers.ContentLength = contentBytes.Length; + + var requestIdentifierFeature = new HttpRequestIdentifierFeature + { + TraceIdentifier = "Ihr TraceIdentifier-Wert" + }; + + var connectionFeature = new HttpConnectionFeature + { + LocalPort = 8080, + LocalIpAddress = IPAddress.Parse("192.168.0.1"), + RemotePort = 8080, + RemoteIpAddress = IPAddress.Parse("127.0.0.1"), + ConnectionId = "0HN50661TV8TP" + }; + + featureCollection.Set(requestFeature); + featureCollection.Set(requestIdentifierFeature); + featureCollection.Set(connectionFeature); + + var componentManager = CreateComponentHubMock(); + var context = new WebMessage.HttpContext(featureCollection, CreateHttpServerContextMock()); + + return context; + } + + /// + /// Creates a mock render context for unit testing. + /// + /// The application context. If null, defaults to null. + /// The scopes of the page. If null, defaults to null. + /// A mock render context for testing. + public static RenderContext CrerateRenderContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) + { + var request = CrerateRequestMock(); + + return new RenderContext(CreratePageContextMock(applicationContext, scopes), request); + } + + /// + /// Create a fake page context for unit testing. + /// + /// The application context. If null, defaults to null. + /// The scopes of the page. + /// A fake context for testing. + public static PageContext CreratePageContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) + { + var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IEndpointManager), typeof(Type), typeof(UriResource), typeof(IUriPathSegment)], null); + + var pageContext = (PageContext)ctorPageContext.Invoke([WebEx.ComponentHub.EndpointManager, null, new UriResource(), null]); + pageContext.ApplicationContext = applicationContext; + pageContext.Scopes = scopes; + + return pageContext; + } + + /// + /// Gets the content of an embedded resource as a string. + /// + /// The name of the resource file. + /// The content of the embedded resource as a string. + public static string GetEmbeddedResource(string fileName) + { + var assembly = typeof(UnitTestControlFixture).Assembly; + var resourceName = assembly.GetManifestResourceNames() + .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); + + using var stream = assembly.GetManifestResourceStream(resourceName); + using var memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + var data = memoryStream.ToArray(); + + return Encoding.UTF8.GetString(data); + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs new file mode 100644 index 0000000..0a4c022 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -0,0 +1,104 @@ +using System.Text; +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebFragment; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the fragment manager. + /// + [Collection("NonParallelTests")] + public class UnitTestFragmentManager + { + /// + /// Test the register function of the fragment manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(9, componentHub.FragmentManager.Fragments.Count()); + } + + /// + /// Test the remove function of the fragment manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var fragmentManager = componentHub.FragmentManager as FragmentManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + fragmentManager.Remove(plugin); + + Assert.Empty(componentHub.FragmentManager.Fragments); + } + + /// + /// Tests whether the fragment manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.EventManager.GetType())); + } + + /// + /// Test the id property of the fragment handler. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] + [InlineData(typeof(TestApplicationB), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] + [InlineData(typeof(TestApplicationC), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] + public void Id(Type applicationType, Type fragmentType, string id) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var fragment = componentHub.FragmentManager.GetFragments(application, fragmentType); + + if (id == null) + { + Assert.Empty(fragment); + return; + } + + Assert.Contains(id, fragment.Select(x => x.FragmentId)); + } + + /// + /// Test the process function of the fragment handler. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA), "TestFragmentA")] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB), "TestFragmentB")] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB), "TestFragmentA")] + public void Process(Type applicationType, Type sectionType, Type scopeType, string expected) + { + // preconditions + var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var renderContext = UnitTestControlFixture.CrerateRenderContextMock(application, [scopeType]); + var builder = new StringBuilder(); + + // test execution + componentHub.FragmentManager.Process(renderContext, sectionType); + renderContext.VisualTree.Content.ToString(builder, 0); + + Assert.Contains(expected, builder.ToString()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs b/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs deleted file mode 100644 index 064c496..0000000 --- a/src/WebExpress.WebCore.Test/Request/UnitTestRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace WebExpress.WebCore.Test.Request -{ - [Collection("NonParallelTests")] - public class UnitTestRequest - { - - [Fact] - public void Get_1() - { - Assert.True - ( - true - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/TestFragmentA.cs b/src/WebExpress.WebCore.Test/TestFragmentA.cs new file mode 100644 index 0000000..73a30a3 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestFragmentA.cs @@ -0,0 +1,55 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test fragment. + /// + [Section()] + [Scope] + [Scope] + [Order(0)] + public sealed class TestFragmentA : IFragment + { + /// + /// Initialization of the fragment. Here, for example, managed resources can be loaded. + /// + /// The component hub. + /// The context of the fragment. + public TestFragmentA(IComponentHub componentHub, IFragmentContext fragmentContext) + { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + + // test the injection + if (fragmentContext == null) + { + throw new ArgumentNullException(nameof(fragmentContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processes the fragments in the specified render context. + /// + /// The context in which rendering occurs. + public void Process(IRenderContext renderContext) + { + renderContext.VisualTree.Content = new HtmlText("TestFragmentA"); + } + + /// + /// Disposes the resources used by the fragment. + /// + public void Dispose() + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestFragmentB.cs b/src/WebExpress.WebCore.Test/TestFragmentB.cs new file mode 100644 index 0000000..8520660 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestFragmentB.cs @@ -0,0 +1,54 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test fragment. + /// + [Section()] + [Scope] + [Order(0)] + public sealed class TestFragmentB : IFragment + { + /// + /// Initialization of the fragment. Here, for example, managed resources can be loaded. + /// + /// The component hub. + /// The context of the fragment. + public TestFragmentB(IComponentHub componentHub, IFragmentContext fragmentContext) + { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + + // test the injection + if (fragmentContext == null) + { + throw new ArgumentNullException(nameof(fragmentContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processes the fragments in the specified render context. + /// + /// The context in which rendering occurs. + public void Process(IRenderContext renderContext) + { + renderContext.VisualTree.Content = new HtmlText("TestFragmentB"); + } + + /// + /// Disposes the resources used by the fragment. + /// + public void Dispose() + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPageB.cs b/src/WebExpress.WebCore.Test/TestPageB.cs index 9de34b5..9fe82e0 100644 --- a/src/WebExpress.WebCore.Test/TestPageB.cs +++ b/src/WebExpress.WebCore.Test/TestPageB.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebScope; namespace WebExpress.WebCore.Test { @@ -10,7 +11,7 @@ namespace WebExpress.WebCore.Test [Segment("pageb", "webindex:homepage.label")] [ContextPath(null)] [Parent] - public sealed class TestPageB : IPage + public sealed class TestPageB : IPage, IScope { /// /// Returns or sets the title of the page. diff --git a/src/WebExpress.WebCore.Test/TestScopeA.cs b/src/WebExpress.WebCore.Test/TestScopeA.cs new file mode 100644 index 0000000..0159221 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestScopeA.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebScope; + +namespace WebExpress.WebCore.Test +{ + /// + /// Test scope B implementing the IScope interface. + /// + internal class TestScopeB : IScope + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/TestScopeB.cs b/src/WebExpress.WebCore.Test/TestScopeB.cs new file mode 100644 index 0000000..3e699d9 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestScopeB.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebScope; + +namespace WebExpress.WebCore.Test +{ + /// + /// Test scope A implementing the IScope interface. + /// + internal class TestScopeA : IScope + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/TestSectionA.cs b/src/WebExpress.WebCore.Test/TestSectionA.cs new file mode 100644 index 0000000..42bbde3 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSectionA.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebSection; + +namespace WebExpress.WebCore.Test +{ + /// + /// Test section A implementing the ISection interface. + /// + public class TestSectionA : ISection + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs index 48e89ae..9d2e010 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs @@ -28,7 +28,7 @@ public static string I18N(this RenderContext obj, string pluginId, string key) /// The value of the key in the current language. public static string I18N(this RenderContext obj, string key) { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.ApplicationContext?.PluginContext?.PluginId, key); + return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.PageContext?.PluginContext?.PluginId, key); } /// diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 4a43fc6..3b1ed1a 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -133,6 +133,15 @@ jobmanager.job.process=Der Job '{0}' wird ausgeführt. jobmanager.cron.parseerror=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' kann nicht verarbeitet werden. jobmanager.cron.range=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' ist außerhalb des gültigen Bereiches. +fragmentmanager.initialization=Der Fragmentmanager wurde initialisiert. +fragmentmanager.register=Das Fragment '{0}' wurde in der Sektion '{1}' der Anwendung '{2}' registriert. +fragmentmanager.error.section=Die Angabe der Sektion ist fehlerhaft. +fragmentmanager.wrongtype=Der Typ '{0}' implementiert die Schnittstelle '{1}' nicht. +fragmentmanager.moduleless=Das Fragment '{0}' aus dem Plugin '{1}' besitzt keine Angaben zum Modul. +fragmentmanager.addfragment.duplicate=Das Fragment '{0}' aus dem Plugin '{1}' ist bereits hinzugefügt worden. +fragmentmanager.titel=Fragmente: +fragmentmanager.fragment={0} + identitymanager.initialization=Der Identitymanager wurde initialisiert. identitymanager.registerpermission=Die Berechtigung '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. identitymanager.duplicatepermission=Die Berechtigung '{0}' wurde bereits in der Anwendung '{1}' registriert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index a587386..d336605 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -133,6 +133,15 @@ jobmanager.job.process=The job '{0}' is executed. jobmanager.cron.parseerror=Syntax error in the timing of a job. The value '{0}' cannot be processed. jobmanager.cron.range=Syntax error in the timing of a job. The value '{0}' is outside the valid range. +fragmentmanager.initialization=The fragment manager has been initialized. +fragmentmanager.register=The fragment '{0}' has been registered in the '{1}' section of application '{2}'. +fragmentmanager.error.section=The section is incorrect. +fragmentmanager.wrongtype=The type '{0}' does not implement the interface '{1}'. +fragmentmanager.moduleless=The fragment '{0}' from the plugin '{1}' has no information about the module. +fragmentmanager.addfragment.duplicate=The fragment '{0}' from the plugin '{1}' has already been added. +fragmentmanager.titel=Fragments: +fragmentmanager.fragment={0} + identitymanager.initialization=The identity manager has been initialized. identitymanager.registerpermission=The permission '{0}' has been assigned to the application '{1}' and registered in the identity manager. identitymanager.duplicatepermission=The permission '{0}' has already been registered in the application '{1}'. diff --git a/src/WebExpress.WebCore/WebAttribute/IFragmentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IFragmentAttribute.cs new file mode 100644 index 0000000..b8f1860 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IFragmentAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Marks a class as a fragment component. + /// + public interface IFragmentAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs new file mode 100644 index 0000000..740f8a8 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs @@ -0,0 +1,17 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute used to identify a class as a plugin component. + /// + public class OrderAttribute : System.Attribute, IFragmentAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The order within the section. + public OrderAttribute(int order) + { + } + + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs index 7785831..a2dd838 100644 --- a/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ScopeAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// The range in which the component is valid. /// - [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ScopeAttribute : Attribute, IPageAttribute, ISettingPageAttribute where T : class, IScope { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs new file mode 100644 index 0000000..d2ff7b3 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs @@ -0,0 +1,19 @@ +using System; +using WebExpress.WebCore.WebSection; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to identify a section. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public class SectionAttribute : Attribute, IFragmentAttribute where T : class, ISection + { + /// + /// Initializes a new instance of the class. + /// + public SectionAttribute() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 7d2e573..a55cdd8 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -7,6 +7,7 @@ using WebExpress.WebCore.WebComponent.Model; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebFragment; using WebExpress.WebCore.WebIdentity; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; @@ -42,6 +43,7 @@ public class ComponentHub : IComponentHub private readonly SettingPageManager _settingPageManager; private readonly RestApiManager _restApiManager; private readonly SitemapManager _sitemapManager; + private readonly FragmentManager _fragmentManager; private readonly StatusPageManager _statusPageManager; private readonly SessionManager _sessionManager; private readonly EventManager _eventManager; @@ -70,6 +72,7 @@ public class ComponentHub : IComponentHub _applicationManager, _endpointManager, _sitemapManager, + _fragmentManager, _assetManager, _resourceManager, _pageManager, @@ -168,6 +171,12 @@ public class ComponentHub : IComponentHub /// The instance of the sitemap manager. public ISitemapManager SitemapManager => _sitemapManager; + /// + /// Returns the fragment manager. + /// + /// The instance of the fragment manager. + public IFragmentManager FragmentManager => _fragmentManager; + /// /// Returns the status page manager. /// @@ -208,6 +217,7 @@ internal ComponentHub(IHttpServerContext httpServerContext) _internationalizationManager = CreateInstance(typeof(InternationalizationManager)) as InternationalizationManager; _applicationManager = CreateInstance(typeof(ApplicationManager)) as ApplicationManager; _sitemapManager = CreateInstance(typeof(SitemapManager)) as SitemapManager; + _fragmentManager = CreateInstance(typeof(FragmentManager)) as FragmentManager; _endpointManager = CreateInstance(typeof(EndpointManager)) as EndpointManager; _assetManager = CreateInstance(typeof(AssetManager)) as AssetManager; _resourceManager = CreateInstance(typeof(ResourceManager)) as ResourceManager; diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 5e008f1..c1cc45f 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -5,6 +5,7 @@ using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebEvent; +using WebExpress.WebCore.WebFragment; using WebExpress.WebCore.WebIdentity; using WebExpress.WebCore.WebJob; using WebExpress.WebCore.WebLog; @@ -125,6 +126,12 @@ public interface IComponentHub : IComponentManager /// The instance of the sitemap manager. ISitemapManager SitemapManager { get; } + /// + /// Returns the fragment manager. + /// + /// The instance of the fragment manager. + IFragmentManager FragmentManager { get; } + /// /// Returns the status page manager. /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs b/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs new file mode 100644 index 0000000..57a5939 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace WebExpress.WebCore.WebFragment +{ + [Obsolete] + public class FragmentComparer : IEqualityComparer + { + /// + /// Tests for equality. + /// + /// The first object to compare. + /// The second object to compare. + /// True if both objects are similar; false otherwise. + public bool Equals(T x, T y) + { + if (x == null && y == null) + { + return true; + } + else if (x.GetType() == y.GetType()) + { + return true; + } + + return false; + } + + /// + /// Returns a hash code for the specified object. + /// + /// The object for which to return a hash code. + /// The hash code. + public int GetHashCode([DisallowNull] T obj) + { + return obj.GetType().GetHashCode(); + } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs new file mode 100644 index 0000000..021cca0 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Represents the context for a web fragment, including plugin context, application context, + /// conditions for activation, culture information, and caching behavior. + /// + public class FragmentContext : IFragmentContext + { + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Gets the unique identifier for the fragment. + /// + public string FragmentId { get; internal set; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + public ICollection Conditions { get; internal set; } = []; + + /// + /// Determines whether the component is created once and reused on each execution. + /// + public bool Cache { get; internal set; } + + /// + /// Returns the section. + /// + public Type Section { get; internal set; } + + /// + /// Returns the scope. + /// + public Type Scope { get; internal set; } + + /// + /// Initializes a new instance of the class. + /// + public FragmentContext() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs new file mode 100644 index 0000000..c44c87a --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -0,0 +1,501 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebFragment.Model; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebScope; +using WebExpress.WebCore.WebSection; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// The fragment manager. Fragments are independent parts of a page. + /// + public sealed class FragmentManager : IFragmentManager + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly FragmentDictionary _dictionary = []; + + /// + /// An event that fires when an fragment is added. + /// + public event EventHandler AddFragment; + + /// + /// An event that fires when an fragment is removed. + /// + public event EventHandler RemoveFragment; + + /// + /// Returns the collection of fragment contexts. + /// + public IEnumerable Fragments => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.FragmentContext); + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The reference to the context of the host. + private FragmentManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + _httpServerContext = httpServerContext; + + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress.webcore:fragmentmanager.initialization") + ); + } + + /// + /// Discovers and binds fragments to an application. + /// + /// The context of the plugin whose fragments are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_dictionary.ContainsKey(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds fragments to an application. + /// + /// The context of the application whose fragments are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers pages for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext.Assembly; + + foreach (var fragmentType in assembly.GetTypes().Where + ( + x => x.IsClass && + x.IsSealed && + x.IsPublic && + ( + x.GetInterfaces().Contains(typeof(IFragment)) || + x.GetInterfaces().Contains(typeof(IFragmentDynamic)) + ) + )) + { + var id = fragmentType.FullName?.ToLower(); + var scopes = new List(); + var sections = new List(); + var conditions = new List(); + var cache = false; + var order = 0; + + // determining attributes + foreach (var customAttribute in fragmentType.CustomAttributes.Where + ( + x => x.AttributeType.GetInterfaces() + .Contains(typeof(IEndpointAttribute)) + )) + { + if (customAttribute.AttributeType.Name == typeof(ScopeAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) + { + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()); + } + else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) + { + var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + conditions.Add(Activator.CreateInstance(condition) as ICondition); + } + else if (customAttribute.AttributeType == typeof(CacheAttribute)) + { + cache = true; + } + } + + foreach (var customAttribute in fragmentType.CustomAttributes.Where + ( + x => x.AttributeType.GetInterfaces().Contains(typeof(IFragmentAttribute)) + )) + { + if (customAttribute.AttributeType.Name == typeof(SectionAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(SectionAttribute<>).Namespace) + { + sections.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()); + } + else if (customAttribute.AttributeType == typeof(OrderAttribute)) + { + try + { + order = Convert.ToInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); + } + catch + { + } + } + } + + // check section + if (sections.Count == 0) + { + _httpServerContext.Log.Warning(I18N.Translate + ( + "webexpress.webcore:fragmentmanager.error.section" + )); + + continue; + } + + // assign the fragment to existing applications + foreach (var applicationContext in _componentHub.ApplicationManager.GetApplications(pluginContext)) + { + // assign section + foreach (var section in sections) + { + // assign scope + foreach (var scope in scopes) + { + var fragmentContext = new FragmentContext() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + FragmentId = id, + Cache = cache, + Section = section, + Scope = scope, + Conditions = conditions + }; + + var fragmentItem = new FragmentItem(_componentHub, _httpServerContext) + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + FragmentContext = fragmentContext, + FragmentClass = fragmentType, + Order = order, + Cache = cache, + Conditions = conditions, + Section = section, + Scope = scope + }; + + if (_dictionary.AddFragmentItem(pluginContext, applicationContext, fragmentItem)) + { + OnAddFragment(fragmentContext); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress.webcore:fragmentmanager.register", + id, + section, + applicationContext.ApplicationId + ) + ); + } + } + } + } + } + + Log(); + } + + /// + /// Removes all components associated with the specified plugin context. + /// + /// The context of the plugin that contains the components to remove. + internal void Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return; + } + + var fragments = _dictionary.RemoveFragments(pluginContext); + + foreach (var fragment in fragments) + { + OnRemoveFragment(fragment); + } + } + + /// + /// Removes all fragments associated with the specified application context. + /// + /// The context of the application that contains the fragments to remove. + internal void Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return; + } + + var fragments = _dictionary.RemoveFragments(applicationContext); + + foreach (var fragment in fragments) + { + OnRemoveFragment(fragment); + } + } + + /// + /// Raises the AddFragment event. + /// + /// The fragment context. + private void OnAddFragment(IFragmentContext fragmentContext) + { + AddFragment?.Invoke(this, fragmentContext); + } + + /// + /// Raises the RemoveFragment event. + /// + /// The fragment context. + private void OnRemoveFragment(IFragmentContext fragmentContext) + { + RemoveFragment?.Invoke(this, fragmentContext); + } + + /// + /// Raises the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Raises the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + + /// + /// Raises the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments() where T : IFragment + { + return GetFragments(typeof(T)); + } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(Type fragmentType) + { + return _dictionary.Values + .SelectMany(x => x) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == fragmentType) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type.. + /// The application context. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragment + { + return GetFragments(applicationContext, typeof(T)); + } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The application context. + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, Type fragmentType) + { + return _dictionary.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == fragmentType) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. + /// The application context. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext) where S : ISection where T : IScope + { + return GetFragments(applicationContext, typeof(S), typeof(T)); + } + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The application context. + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope) + { + return _dictionary.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == section) + .SelectMany(x => x.Value) + .Where(x => x.Key == scope) + .SelectMany(x => x.Value) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The application context. + /// The section where the fragment is embedded. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, IEnumerable scopes) + { + foreach (var scope in scopes) + { + foreach (var item in GetFragments(applicationContext, section, scope)) + { + yield return item; + } + } + } + + /// + /// Processes the fragments for a given section within the specified render context. + /// + /// The context in which rendering occurs. + /// The section where the fragment is embedded. + public void Process(IRenderContext renderContext, Type section) + { + var scopes = renderContext?.PageContext?.Scopes ?? []; + + var items = _dictionary.Values + .SelectMany(x => x) + .Where(x => x.Key == renderContext?.PageContext?.ApplicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == section) + .SelectMany(x => x.Value) + .Where(x => scopes.Any(y => x.Key == y)) + .SelectMany(x => x.Value) + .OrderBy(x => x.Order); + + foreach (var item in items) + { + item.Process(renderContext); + } + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + private void Log() + { + //output.Add + //( + // string.Empty.PadRight(deep) + + // I18N.Translate("webexpress.webui:fragmentmanager.titel") + //); + + //foreach (var fragmentItem in GetFragmentItems(pluginContext)) + //{ + // output.Add + // ( + // string.Empty.PadRight(deep + 2) + + // I18N.Translate + // ( + // "webexpress.webui:fragmentmanager.fragment", + // fragmentItem.FragmentClass.Name + // ) + // ); + //} + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); + } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/IFragment.cs b/src/WebExpress.WebCore/WebFragment/IFragment.cs new file mode 100644 index 0000000..26c6cde --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/IFragment.cs @@ -0,0 +1,17 @@ +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Represents a fragment that is a part of a web component. + /// + public interface IFragment : IComponent + { + /// + /// Processes the fragments in the specified render context. + /// + /// The context in which rendering occurs. + void Process(IRenderContext renderContext); + } +} diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs b/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs new file mode 100644 index 0000000..be9a653 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Interface representing the context of a web fragment. + /// Provides access to plugin context, application context, conditions for activation, and caching behavior. + /// + public interface IFragmentContext : IContext + { + /// + /// Returns the context of the associated plugin. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the application context. + /// + IApplicationContext ApplicationContext { get; } + + /// + /// Gets the unique identifier for the fragment. + /// + string FragmentId { get; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + ICollection Conditions { get; } + + /// + /// Returns the section. + /// + Type Section { get; } + + /// + /// Returns the scope. + /// + Type Scope { get; } + + /// + /// Determines whether the component is created once and reused on each execution. + /// + bool Cache { get; } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs b/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs new file mode 100644 index 0000000..f86754e --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.WebFragment +{ + public interface IFragmentDynamic + { + /// + /// Returns the context of the fragment.. + /// + IFragmentContext Context { get; } + + /// + /// Initialization + /// + /// The context. + /// The page where the fragment is active. + void Initialization(IFragmentContext context, IPage page); + + /// + /// Creates fragments of a common type T. + /// + /// The created instances of the fragments. + IEnumerable Create() where T : IComponent; + } +} diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs new file mode 100644 index 0000000..e7bf536 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebScope; +using WebExpress.WebCore.WebSection; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Interface for managing web fragments. + /// + public interface IFragmentManager : IComponentManager + { + /// + /// An event that fires when a fragment is added. + /// + event EventHandler AddFragment; + + /// + /// An event that fires when a fragment is removed. + /// + event EventHandler RemoveFragment; + + /// + /// Returns the collection of fragment contexts. + /// + IEnumerable Fragments { get; } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type.. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments() where T : IFragment; + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(Type fragmentType); + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type.. + /// The application context. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragment; + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The application context. + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext, Type fragmentType); + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. + /// The application context. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext) where S : ISection where T : IScope; + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The application context. + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope); + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The application context. + /// The section where the fragment is embedded. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext, Type section, IEnumerable scopes); + + /// + /// Processes the fragments for a given section within the specified render context. + /// + /// The context in which rendering occurs. + /// The section where the fragment is embedded. + void Process(IRenderContext renderContext, Type section); + } +} diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs new file mode 100644 index 0000000..21f1ca2 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment.Model +{ + /// + /// Represents a dictionary that maps plugin contexts to application contexts, + /// which in turn maps to a dictionary of section types and inner maps of scope types and lists of FragmentItem objects. + /// Plugin -> Application -> Section -> Scope -> FragmentItem + /// + internal class FragmentDictionary : Dictionary>>>> + { + /// + /// Adds a fragment item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The fragment item. + /// True if the fragment item was added successfully, false if an element with the same status code already exists. + public bool AddFragmentItem(IPluginContext pluginContext, IApplicationContext applicationContext, FragmentItem fragmentItem) + { + var type = fragmentItem.FragmentClass; + + if (!typeof(IFragment).IsAssignableFrom(type)) + { + return false; + } + + if (!TryGetValue(pluginContext, out var applicationDict)) + { + applicationDict = []; + this[pluginContext] = applicationDict; + } + + if (!applicationDict.TryGetValue(applicationContext, out var sectionDict)) + { + sectionDict = []; + applicationDict[applicationContext] = sectionDict; + } + + if (!sectionDict.TryGetValue(fragmentItem.Section, out var scopeDict)) + { + scopeDict = []; + sectionDict[fragmentItem.Section] = scopeDict; + } + + if (!scopeDict.TryGetValue(fragmentItem.Scope, out var itemList)) + { + itemList = []; + scopeDict[fragmentItem.Scope] = itemList; + } + + if (!itemList.Any(x => x.FragmentClass == fragmentItem.FragmentClass)) + { + itemList.Add(fragmentItem); + + return true; + } + + return false; // item with the same fragment class already exists + } + + /// + /// Removes fragments from the dictionary. + /// + /// The plugin context. + /// An IEnumerable of fragment contexts that were removed. + public IEnumerable RemoveFragments(IPluginContext pluginContext) + { + var fragments = GetFragments(pluginContext); + + Remove(pluginContext); + + return fragments; + } + + /// + /// Removes fragments from the dictionary. + /// + /// The application context. + /// An IEnumerable of fragment contexts that were removed. + public IEnumerable RemoveFragments(IApplicationContext applicationContext) + { + foreach (var pluginKeyValue in this) + { + if (pluginKeyValue.Value.TryGetValue(applicationContext, out var sectionDict)) + { + pluginKeyValue.Value.Remove(applicationContext); + + if (pluginKeyValue.Value.Count == 0) + { + Remove(pluginKeyValue.Key); + } + + foreach (var item in sectionDict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.FragmentContext)) + { + yield return item; + } + } + } + } + + /// + /// Returns the fragment items from the dictionary. + /// + /// The type of fragment. + /// The application context. + /// An IEnumerable of fragment items + public IEnumerable GetFragmentItems(IApplicationContext applicationContext) where T : IFragment + { + return GetFragmentItems(applicationContext, typeof(T)); + } + + /// + /// Returns the fragment items from the dictionary. + /// + /// The application context. + /// The type of fragment. + /// An IEnumerable of fragment items + public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type fragmentType) + { + if (!typeof(IFragment).IsAssignableFrom(fragmentType)) + { + return []; + } + + return Values + .Where(x => x.ContainsKey(applicationContext)) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Where(x => x.FragmentClass == fragmentType); + } + + /// + /// Returns all fragment contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of fragment contexts. + public IEnumerable GetFragments(IPluginContext pluginContext) + { + return this.Where(x => x.Key == pluginContext) + .SelectMany(x => x.Value.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.FragmentContext); + } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs new file mode 100644 index 0000000..42e1b73 --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment.Model +{ + /// + /// Fragments are components that can be integrated into pages to dynamically expand functionalities. + /// + internal class FragmentItem : IDisposable + { + private IFragment _instance; + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; set; } + + /// + /// Returns the fragment context. + /// + public IFragmentContext FragmentContext { get; set; } + + /// + /// The type of fragment. + /// + public Type FragmentClass { get; set; } + + /// + /// Returns the section. + /// + public Type Section { get; set; } + + /// + /// Returns the scope. + /// + public Type Scope { get; set; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + public ICollection Conditions { get; set; } + + /// + /// The order of the fragment. + /// + public int Order { get; set; } + + /// + /// Determines whether the component is created once and reused on each execution. + /// + public bool Cache { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The fragment manager responsible for managing web fragments. + /// The context of the HTTP server. + public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + _httpServerContext = httpServerContext; + } + /// + /// Processes the fragments for a given section within the specified render context. + /// + /// The context in which rendering occurs. + public void Process(IRenderContext renderContext) + { + var instance = _instance; + if (instance == null) + { + instance = ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + } + + if (CheckControl(renderContext)) + { + instance.Process(renderContext); + } + + if (Cache) + { + _instance = instance; + } + } + + /// + /// Checks the component to see if they are displayed or disabled. + /// + /// The context in which checking occurs. + /// True if the fragment is active, false otherwise. + private bool CheckControl(IRenderContext renderContext) + { + return !FragmentContext.Conditions.Any() || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Convert the resource element to a string. + /// + /// The resource element in its string representation. + public override string ToString() + { + return $"Fragment: '{FragmentContext.FragmentId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index 3c6f18d..12e91b0 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebPage @@ -18,6 +19,6 @@ public interface IPageContext : IEndpointContext /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// - IEnumerable Scopes { get; } + IEnumerable Scopes { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/IRenderContext.cs b/src/WebExpress.WebCore/WebPage/IRenderContext.cs index 733c577..aa62137 100644 --- a/src/WebExpress.WebCore/WebPage/IRenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/IRenderContext.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebPage { @@ -10,20 +8,15 @@ namespace WebExpress.WebCore.WebPage public interface IRenderContext { /// - /// Returns the application context. + /// Returns the page context. /// - IApplicationContext ApplicationContext { get; } + IPageContext PageContext { get; } /// /// Returns the request. /// Request Request { get; } - /// - /// Returns the scopes. - /// - IEnumerable Scopes { get; } - /// /// Returns the contents of a page. /// diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 8fec312..51f199f 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -37,7 +37,7 @@ internal class PageItem : IDisposable /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// - public IReadOnlyList Scopes { get; set; } + public IEnumerable Scopes { get; set; } /// /// Returns or sets the paths of the resource. diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index c53e478..c2249ab 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -34,7 +34,7 @@ public class PageContext : IPageContext /// is a string with a name (e.g. global, admin), which can be used by elements to /// determine whether content and how content should be displayed. /// - public IEnumerable Scopes { get; internal set; } = []; + public IEnumerable Scopes { get; internal set; } = []; /// /// Returns the conditions that must be met for the resource to be active. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index b1f196c..a870d51 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -76,7 +76,7 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon } else { - context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); + context = new RenderContext(pageContetx, request); } page.Process(context); @@ -285,7 +285,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); + var scopes = new List(); var conditions = new List(); var cache = false; @@ -328,13 +328,13 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) { - scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()); } } if (resourceType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) { - scopes.Add(resourceType.FullName?.ToLower()); + scopes.Add(resourceType); } // assign the page to existing applications diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 5c19ecd..4f08556 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Globalization; -using WebExpress.WebCore.WebApplication; +using System.Globalization; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; @@ -12,9 +10,9 @@ namespace WebExpress.WebCore.WebPage public class RenderContext : IRenderContext { /// - /// Returns the application context. + /// Returns the page context. /// - public IApplicationContext ApplicationContext { get; protected set; } + public IPageContext PageContext { get; protected set; } /// /// Returns the request. @@ -31,11 +29,6 @@ public class RenderContext : IRenderContext /// public CultureInfo Culture => Request?.Culture; - /// - /// Returns the scopes. - /// - public IEnumerable Scopes { get; } - /// /// Returns the contents of a page. /// @@ -52,15 +45,13 @@ public RenderContext() /// /// Initializes a new instance of the class. /// - /// >The application context. + /// >The page context. /// The request associated with the rendering context. - /// The scopes associated with the rendering context. - public RenderContext(IApplicationContext applicationContext, Request request, IEnumerable scopes) + public RenderContext(IPageContext pageContext, Request request) : this() { - ApplicationContext = applicationContext; + PageContext = pageContext; Request = request; - Scopes = scopes; } /// @@ -68,7 +59,7 @@ public RenderContext(IApplicationContext applicationContext, Request request, IE /// /// The context to copy. public RenderContext(RenderContext context) - : this(context?.ApplicationContext, context?.Request, context?.Scopes) + : this(context?.PageContext, context?.Request) { } diff --git a/src/WebExpress.WebCore/WebSection/ISection.cs b/src/WebExpress.WebCore/WebSection/ISection.cs new file mode 100644 index 0000000..98cd4da --- /dev/null +++ b/src/WebExpress.WebCore/WebSection/ISection.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebSection +{ + /// + /// Interface of a section. + /// + public interface ISection + { + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index b3376d0..51a8c7b 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -73,7 +73,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe } else { - context = new RenderContext(endpontContext?.ApplicationContext, request, pageContetx.Scopes); + context = new RenderContext(pageContetx, request); } settingPage.Process(context); From 948119036604368bdd063845db7e911418c78667 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 4 Dec 2024 22:15:58 +0100 Subject: [PATCH 046/162] bug fixes --- .../Fixture/UnitTestControlFixture.cs | 13 +--- .../Manager/UnitTestApplication.cs | 12 +-- .../Manager/UnitTestAssetManager.cs | 54 ++++++------- ...mponent.cs => UnitTestComponentManager.cs} | 2 +- .../{UnitTestLog.cs => UnitTestLogManager.cs} | 2 +- ...stPackage.cs => UnitTestPackageManager.cs} | 2 +- .../Manager/UnitTestPageManager.cs | 18 ++--- ...TestPlugin.cs => UnitTestPluginManager.cs} | 4 +- .../Manager/UnitTestResourceManager.cs | 24 +++--- .../Manager/UnitTestRestApiManager.cs | 18 ++--- .../Manager/UnitTestSettingPageManager.cs | 12 +-- .../Manager/UnitTestSitemapManager.cs | 77 ++++++++++--------- .../Manager/UnitTestStatusPageManager.cs | 18 ++--- src/WebExpress.WebCore.Test/TestPageC.cs | 2 +- .../Uri/UnitTestUriRelative.cs | 4 +- .../WebPlugin/PluginManager.cs | 23 +++--- .../WebSitemap/Model/SitemapNode.cs | 2 +- .../WebSitemap/SitemapManager.cs | 8 +- .../Model/StatusPageDictionary.cs | 2 +- src/WebExpress.WebCore/WebUri/UriResource.cs | 6 +- 20 files changed, 151 insertions(+), 152 deletions(-) rename src/WebExpress.WebCore.Test/Manager/{UnitTestComponent.cs => UnitTestComponentManager.cs} (95%) rename src/WebExpress.WebCore.Test/Manager/{UnitTestLog.cs => UnitTestLogManager.cs} (97%) rename src/WebExpress.WebCore.Test/Manager/{UnitTestPackage.cs => UnitTestPackageManager.cs} (97%) rename src/WebExpress.WebCore.Test/Manager/{UnitTestPlugin.cs => UnitTestPluginManager.cs} (98%) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs index adb9d43..528542e 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs @@ -11,7 +11,6 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Fixture @@ -23,12 +22,6 @@ public class UnitTestControlFixture : IDisposable { private static readonly string[] _separator = ["\r\n", "\r", "\n"]; - /// - /// Returns the list of resources. - /// - private static List Ressources { get; } = []; - - /// /// Initializes a new instance of the class and boot the component manager. /// @@ -50,7 +43,7 @@ public static IHttpServerContext CreateHttpServerContextMock() Environment.CurrentDirectory, Environment.CurrentDirectory, Environment.CurrentDirectory, - null, + new UriResource("/server"), CultureInfo.GetCultureInfo("en"), new Log() { LogMode = LogMode.Off }, null @@ -102,7 +95,7 @@ public static ComponentHub CreateAndRegisterComponentHubMock() /// The content of the request. /// The URI of the request. /// A fake request for testing. - public static WebMessage.Request CrerateRequestMock(string content = "", string uri = "") + public static Request CrerateRequestMock(string content = "", string uri = "") { var context = CreateHttpContextMock(content); @@ -123,7 +116,7 @@ public static WebMessage.Request CrerateRequestMock(string content = "", string /// A fake http context for testing. public static WebMessage.HttpContext CreateHttpContextMock(string content = "") { - var ctorRequest = typeof(WebMessage.Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); + var ctorRequest = typeof(Request).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IFeatureCollection), typeof(RequestHeaderFields), typeof(IHttpServerContext)], null); var featureCollection = new FeatureCollection(); var firstLine = content.Split('\n').FirstOrDefault(); var lines = content.Split(_separator, StringSplitOptions.None); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index 61be7f0..ebdfdd3 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -102,9 +102,9 @@ public void Description(Type applicationType, string description) /// Test the icon property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/appa/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationB), "/appb/assets/img/Logo.png")] - [InlineData(typeof(TestApplicationC), "/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationA), "/server/appa/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationB), "/server/appb/assets/img/Logo.png")] + [InlineData(typeof(TestApplicationC), "/server/assets/img/Logo.png")] public void Icon(Type applicationType, string icon) { // preconditions @@ -119,9 +119,9 @@ public void Icon(Type applicationType, string icon) /// Test the context path property of the application. /// [Theory] - [InlineData(typeof(TestApplicationA), "/appa")] - [InlineData(typeof(TestApplicationB), "/appb")] - [InlineData(typeof(TestApplicationC), "/")] + [InlineData(typeof(TestApplicationA), "/server/appa")] + [InlineData(typeof(TestApplicationB), "/server/appb")] + [InlineData(typeof(TestApplicationC), "/server")] public void ContextPath(Type applicationType, string contextPath) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs index b95b4a8..f3a394d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs @@ -71,15 +71,15 @@ public void Id(Type applicationType, string id) /// Test the uri property of the asset. /// [Theory] - [InlineData(typeof(TestApplicationA), "/appa/assets/css.mycss.css")] - [InlineData(typeof(TestApplicationA), "/appa/assets/js.myjavascript.js")] - [InlineData(typeof(TestApplicationA), "/appa/assets/js.myjavascript.mini.js")] - [InlineData(typeof(TestApplicationB), "/appb/assets/css.mycss.css")] - [InlineData(typeof(TestApplicationB), "/appb/assets/js.myjavascript.js")] - [InlineData(typeof(TestApplicationB), "/appb/assets/js.myjavascript.mini.js")] - [InlineData(typeof(TestApplicationC), "/assets/css.mycss.css")] - [InlineData(typeof(TestApplicationC), "/assets/js.myjavascript.js")] - [InlineData(typeof(TestApplicationC), "/assets/js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationA), "/server/appa/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationA), "/server/appa/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationA), "/server/appa/assets/js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationB), "/server/appb/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationB), "/server/appb/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationB), "/server/appb/assets/js.myjavascript.mini.js")] + [InlineData(typeof(TestApplicationC), "/server/assets/css.mycss.css")] + [InlineData(typeof(TestApplicationC), "/server/assets/js.myjavascript.js")] + [InlineData(typeof(TestApplicationC), "/server/assets/js.myjavascript.mini.js")] public void Uri(Type applicationType, string uri) { // preconditions @@ -95,24 +95,24 @@ public void Uri(Type applicationType, string uri) /// Test the request of the asset. /// [Theory] - [InlineData("http://localhost:8080/appa/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/appa/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] - [InlineData("http://localhost:8080/appa/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/appa/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] - [InlineData("http://localhost:8080/appb/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/appb/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/appb/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] - [InlineData("http://localhost:8080/appb/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/appb/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/appb/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] - [InlineData("http://localhost:8080/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] - [InlineData("http://localhost:8080/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] - [InlineData("http://localhost:8080/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] - [InlineData("http://localhost:8080/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/appa/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/appb/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/appb/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/appb/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/appb/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/appb/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/appb/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/assets/css/mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/assets/js/myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/assets/js/myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] + [InlineData("http://localhost:8080/server/assets/css.mycss.css", "webexpress.webcore.asset", "css.mycss.css")] + [InlineData("http://localhost:8080/server/assets/js.myjavascript.js", "webexpress.webcore.asset", "js.myjavascript.js")] + [InlineData("http://localhost:8080/server/assets/js.myjavascript.mini.js", "webexpress.webcore.asset", "js.myjavascript.mini.js")] public void Request(string uri, string id, string resource) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs similarity index 95% rename from src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs rename to src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs index a02e06d..c5caca2 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestComponent.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.Test.Manager /// Test the component manager. /// [Collection("NonParallelTests")] - public class UnitTestComponent + public class UnitTestComponentManager { /// /// Test the plugin manager property of the component manager. diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs similarity index 97% rename from src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs rename to src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs index e695909..988299c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestLog.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.Test.Manager /// Test the log manager. /// [Collection("NonParallelTests")] - public class UnitTestLog + public class UnitTestLogManager { /// /// Test the register function of the log manager. diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs similarity index 97% rename from src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs rename to src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs index bb8cc67..242c2ce 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPackage.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.Test.Manager /// Test the package manager. /// [Collection("NonParallelTests")] - public class UnitTestPackage + public class UnitTestPackageManager { /// /// Test the register function of the package manager. diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index cd422bf..057855e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -93,15 +93,15 @@ public void Title(Type applicationType, Type resourceType, string id) /// Test the context path property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestPageA), "/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/appa/resa")] - [InlineData(typeof(TestApplicationA), typeof(TestPageC), "/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestPageA), "/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/appb/resa")] - [InlineData(typeof(TestApplicationB), typeof(TestPageC), "/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/")] - [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/resa")] - [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/")] + [InlineData(typeof(TestApplicationA), typeof(TestPageA), "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/server/appa/resa")] + [InlineData(typeof(TestApplicationA), typeof(TestPageC), "/server/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestPageA), "/server/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/server/appb/resa")] + [InlineData(typeof(TestApplicationB), typeof(TestPageC), "/server/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/server")] + [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/server/resa")] + [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/server")] public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs similarity index 98% rename from src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs rename to src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs index f4a608f..43d4534 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPlugin.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.Test.Manager /// Test the plugin manager. /// [Collection("NonParallelTests")] - public class UnitTestPlugin + public class UnitTestPluginManager { /// /// Test the register function of the plugin manager. @@ -175,7 +175,7 @@ public void GetIcon() var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - Assert.Equal("/assets/img/Logo.png", plugin.Icon); + Assert.Equal("/server/assets/img/Logo.png", plugin.Icon); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index b7083c5..23304aa 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -71,18 +71,18 @@ public void Id(Type applicationType, Type resourceType, string id) /// Test the context path property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "/appa/resa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "/appb/resa")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "/")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "/resa")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "/server/appa/resa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "/server/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "/server/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "/server/appb/resa")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "/server/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "/server/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "/server")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "/server/resa")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/server")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/server")] public void ContextPath(Type applicationType, Type resourceType, string id) { diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 2e64f9a..86d533d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -68,15 +68,15 @@ public void Id(Type applicationType, Type resourceType, string id) /// Test the context path property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "/appa/1")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "/appa/1/apia/2")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "/appa/1/apia/2/apib/3")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "/appb/1")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "/appb/1/apia/2")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "/appb/1/apia/2/apib/3")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/1")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/1/apia/2")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/1/apia/2/apib/3")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "/server/appa/1")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "/server/appa/1/apia/2")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "/server/appa/1/apia/2/apib/3")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "/server/appb/1")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "/server/appb/1/apia/2")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "/server/appb/1/apia/2/apib/3")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/server/1")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/server/1/apia/2")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/server/1/apia/2/apib/3")] public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 1217cd6..224ee04 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -87,12 +87,12 @@ public void Title(Type applicationType, Type resourceType, string id) /// Test the context path property of the setting page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "/server/appa")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "/server/appb")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/server/appb")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/server")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/server")] public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index fd8d84a..391680e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,43 +22,44 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(45, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(43, componentManager.SitemapManager.SiteMap.Count()); } /// /// Test the SearchResource function of the sitemap. /// [Theory] - [InlineData("http://localhost:8080/appa/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/appa/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/appa/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/appa/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/appb/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/appb/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/appb/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/appb/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/appa/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/appa/resa/pageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/appa/pagec", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/appb/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/appb/resa/pageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/appb/pagec", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/resa/pageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/pagec", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/appa/1/apia", "webexpress.webcore.test.testrestapia")] - [InlineData("http://localhost:8080/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] - [InlineData("http://localhost:8080/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] - [InlineData("http://localhost:8080/appa/assets/css/mycss.css", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/appa/assets/css.mycss.css", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/appa/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/appa/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/appa/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/appb/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/appb/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/appb/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/appb/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/appa/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/appb/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] + [InlineData("http://localhost:8080/server/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] + [InlineData("http://localhost:8080/server/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] + [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/css.mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] public void SearchResource(string uri, string id) @@ -86,13 +87,13 @@ public void SearchResource(string uri, string id) /// Test the GetUri function of the sitemap. /// [Theory] - [InlineData(typeof(TestResourceA), "/appa/resa")] - [InlineData(typeof(TestResourceB), "/appa/resa/resb")] - [InlineData(typeof(TestResourceC), "/appa/resc")] - [InlineData(typeof(TestResourceD), "/appa/resd")] - [InlineData(typeof(TestPageA), "/appa/pagea")] - [InlineData(typeof(TestPageB), "/appa/resa/pageb")] - [InlineData(typeof(TestPageC), "/appa/pagec")] + [InlineData(typeof(TestResourceA), "/server/appa/resa")] + [InlineData(typeof(TestResourceB), "/server/appa/resa/resb")] + [InlineData(typeof(TestResourceC), "/server/appa/resc")] + [InlineData(typeof(TestResourceD), "/server/appa/resd")] + [InlineData(typeof(TestPageA), "/server/appa/pagea")] + [InlineData(typeof(TestPageB), "/server/appa/resa/pageb")] + [InlineData(typeof(TestPageC), "/server")] public void GetUri(Type resourceType, string expected) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index bcf0806..dc9fcdc 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -119,17 +119,17 @@ public void Code(Type applicationType, Type statusPageType, int? code) /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestStatusPage301), null)] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), "/appa/webexpress/icon.png")] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), "/appa/webexpress/icon.png")] - [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), "/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage400), "/server/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage404), "/server/appa/webexpress/icon.png")] + [InlineData(typeof(TestApplicationA), typeof(TestStatusPage500), "/server/appa/webexpress/icon.png")] [InlineData(typeof(TestApplicationB), typeof(TestStatusPage301), null)] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPage400), "/appb/webexpress/icon.png")] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPage404), "/appb/webexpress/icon.png")] - [InlineData(typeof(TestApplicationB), typeof(TestStatusPage500), "/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage400), "/server/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage404), "/server/appb/webexpress/icon.png")] + [InlineData(typeof(TestApplicationB), typeof(TestStatusPage500), "/server/appb/webexpress/icon.png")] [InlineData(typeof(TestApplicationC), typeof(TestStatusPage301), null)] - [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), "/webexpress/icon.png")] - [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), "/webexpress/icon.png")] - [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), "/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), "/server/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), "/server/webexpress/icon.png")] + [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), "/server/webexpress/icon.png")] public void Icon(Type applicationType, Type statusPageType, string icon) { // preconditions diff --git a/src/WebExpress.WebCore.Test/TestPageC.cs b/src/WebExpress.WebCore.Test/TestPageC.cs index cc90bdb..7a4c39c 100644 --- a/src/WebExpress.WebCore.Test/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/TestPageC.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. /// [Title("webindex:pagec.label")] - [Segment("pagec", "webindex:homepage.label")] + [Segment(null, "webindex:homepage.label")] [ContextPath(null)] public sealed class TestPageC : Page { diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs index ced47b7..8d19ac5 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs @@ -56,7 +56,7 @@ public void Test_2() uri.ToString() == str && uri.Scheme == UriScheme.Http && uri.Authority == null && - uri.PathSegments.Count == 1 && + uri.PathSegments.Count == 0 && uri.Fragment == null && uri.Query.Any() == false && uri.IsRelative @@ -94,7 +94,7 @@ public void Test_4() uri.ToString() == str + "/" && uri.Scheme == UriScheme.Http && uri.Authority == null && - uri.PathSegments.Count == 1 && + uri.PathSegments.Count == 0 && uri.Fragment == null && uri.Query.Any() == false && uri.IsRelative diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 263fda9..bef6c4b 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -203,6 +203,17 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex } } + if (plugins.Count > 0) + { + // to many plugins, only one per assembly + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress:pluginmanager.tomany", type.FullName) + ); + + break; + } + if (applicationTypes.Count == 0) { // no application specified @@ -269,17 +280,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex ); } - if (plugins.Count != 0) - { - plugins.Add(pluginContext); - } - else - { - _httpServerContext.Log.Warning - ( - I18N.Translate("webexpress:pluginmanager.tomany", type.FullName) - ); - } + plugins.Add(pluginContext); } } catch (Exception ex) diff --git a/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs index 600f98d..6fa8516 100644 --- a/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs +++ b/src/WebExpress.WebCore/WebSitemap/Model/SitemapNode.cs @@ -138,7 +138,7 @@ public SitemapNode Copy() /// /// Convert to string. /// - /// The tree node in its string representation. + /// The sitemap node in its string representation. public override string ToString() { return Path.FirstOrDefault()?.PathSegment + string.Join diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 37392ba..f1f5dda 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -69,7 +69,7 @@ public void Refresh() { MergeSitemap(newSiteMapNode, CreateSiteMap ( - new Queue(application.PathSegments), + new Queue(application.PathSegments.Where(x => x is not UriPathSegmentRoot)), application.ApplicationContext )); } @@ -87,7 +87,7 @@ public void Refresh() { MergeSitemap(newSiteMapNode, CreateSiteMap ( - new Queue(item.PathSegments), + new Queue(item.PathSegments.Where(x => x is not UriPathSegmentRoot)), item.EndpointContext )); } @@ -279,6 +279,10 @@ IEndpointContext endpointContext { root.Children.Add(next); } + else + { + root.EndpointContext = endpointContext; + } return root; } diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs index c7c0317..7ed442d 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageDictionary.cs @@ -87,7 +87,7 @@ public void RemoveStatusPageItem(IPluginContext pluginContext, IApplicationConte /// The status page item if found, otherwise null. public StatusPageItem GetStatusPageItem(IApplicationContext applicationContext, int statusCode) { - if (ContainsKey(applicationContext?.PluginContext)) + if (applicationContext != null && ContainsKey(applicationContext?.PluginContext)) { var appContextDict = this[applicationContext?.PluginContext]; diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriResource.cs index 12a6333..0cf6a6a 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriResource.cs @@ -165,15 +165,15 @@ public UriResource(UriScheme scheme, UriAuthority authority, string uri) /// The uri. public UriResource(string uri) { - if (uri == null) return; + if (string.IsNullOrWhiteSpace(uri) || uri == "/") return; - if (Enum.GetNames(typeof(UriScheme)).Where(x => uri.StartsWith(x, StringComparison.OrdinalIgnoreCase)).Any()) + if (Enum.GetNames().Where(x => uri.StartsWith(x, StringComparison.OrdinalIgnoreCase)).Any()) { var match = UriRegex().Match(uri); try { - Scheme = (UriScheme)Enum.Parse(typeof(UriScheme), match.Groups[1].Value, true); + Scheme = Enum.Parse(match.Groups[1].Value, true); } catch { From dd668caaeaa6ba832a0c2ca40492440679e421fe Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 4 Dec 2024 22:24:34 +0100 Subject: [PATCH 047/162] refactoring --- .../WebSitemap/SitemapManager.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index f1f5dda..7f2f3c1 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -69,7 +69,7 @@ public void Refresh() { MergeSitemap(newSiteMapNode, CreateSiteMap ( - new Queue(application.PathSegments.Where(x => x is not UriPathSegmentRoot)), + new Queue(application.PathSegments), application.ApplicationContext )); } @@ -87,7 +87,7 @@ public void Refresh() { MergeSitemap(newSiteMapNode, CreateSiteMap ( - new Queue(item.PathSegments.Where(x => x is not UriPathSegmentRoot)), + new Queue(item.PathSegments), item.EndpointContext )); } @@ -210,6 +210,11 @@ private static SitemapNode CreateSiteMap IApplicationContext applicationContext ) { + if (contextPathSegments.Peek() is UriPathSegmentRoot) + { + contextPathSegments.Dequeue(); + } + var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; var next = CreateSiteMap(contextPathSegments, applicationContext, root); @@ -272,6 +277,11 @@ private static SitemapNode CreateSiteMap IEndpointContext endpointContext ) { + if (contextPathSegments.Peek() is UriPathSegmentRoot) + { + contextPathSegments.Dequeue(); + } + var root = new SitemapNode() { PathSegment = new UriPathSegmentRoot() }; var next = CreateSiteMap(contextPathSegments, endpointContext, root); From 39f2d06356342afe023585fc6a9a570adeb5d2b3 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 7 Dec 2024 08:30:35 +0100 Subject: [PATCH 048/162] refactoring --- .../Fixture/NonParallelTestsCollection.cs | 2 +- ...stControlFixture.cs => UnitTestFixture.cs} | 7 ++--- .../Manager/UnitTestApplication.cs | 22 +++++++-------- .../Manager/UnitTestAssetManager.cs | 22 +++++++-------- .../Manager/UnitTestComponentManager.cs | 4 +-- .../Manager/UnitTestEventManager.cs | 14 +++++----- .../Manager/UnitTestFragmentManager.cs | 12 ++++---- .../Manager/UnitTestIdentityManager.cs | 24 ++++++++-------- .../Manager/UnitTestInternationalization.cs | 10 +++---- .../Manager/UnitTestJobManager.cs | 12 ++++---- .../Manager/UnitTestLogManager.cs | 6 ++-- .../Manager/UnitTestPackageManager.cs | 6 ++-- .../Manager/UnitTestPageManager.cs | 14 +++++----- .../Manager/UnitTestPluginManager.cs | 28 +++++++++---------- .../Manager/UnitTestResourceManager.cs | 12 ++++---- .../Manager/UnitTestRestApiManager.cs | 14 +++++----- .../Manager/UnitTestSessionManager.cs | 16 +++++------ .../Manager/UnitTestSettingPageManager.cs | 14 +++++----- .../Manager/UnitTestSitemapManager.cs | 14 +++++----- .../Manager/UnitTestStatusPageManager.cs | 24 ++++++++-------- .../Manager/UnitTestTaskManager.cs | 14 +++++----- .../Message/UnitTestGetRequest.cs | 20 ++++++------- .../Schedule/UnitTestCron.cs | 18 ++++++------ src/WebExpress.WebCore.Test/TestPageA.cs | 9 ------ src/WebExpress.WebCore/WebPage/IPage.cs | 7 ----- 25 files changed, 164 insertions(+), 181 deletions(-) rename src/WebExpress.WebCore.Test/Fixture/{UnitTestControlFixture.cs => UnitTestFixture.cs} (96%) diff --git a/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs b/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs index 0013742..1db3664 100644 --- a/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs +++ b/src/WebExpress.WebCore.Test/Fixture/NonParallelTestsCollection.cs @@ -4,7 +4,7 @@ /// Defines a collection of tests that should not be run in parallel. /// [CollectionDefinition("NonParallelTests", DisableParallelization = true)] - public class NonParallelTestsCollection : ICollectionFixture + public class NonParallelTestsCollection : ICollectionFixture { } diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs similarity index 96% rename from src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs rename to src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index 528542e..72c6764 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestControlFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -18,14 +18,14 @@ namespace WebExpress.WebCore.Test.Fixture /// /// A fixture class for unit tests, providing various mock objects and utility methods. /// - public class UnitTestControlFixture : IDisposable + public class UnitTestFixture : IDisposable { private static readonly string[] _separator = ["\r\n", "\r", "\n"]; /// /// Initializes a new instance of the class and boot the component manager. /// - public UnitTestControlFixture() + public UnitTestFixture() { } @@ -193,7 +193,6 @@ public static RenderContext CrerateRenderContextMock(IApplicationContext applica return new RenderContext(CreratePageContextMock(applicationContext, scopes), request); } - /// /// Create a fake page context for unit testing. /// @@ -218,7 +217,7 @@ public static PageContext CreratePageContextMock(IApplicationContext application /// The content of the embedded resource as a string. public static string GetEmbeddedResource(string fileName) { - var assembly = typeof(UnitTestControlFixture).Assembly; + var assembly = typeof(UnitTestFixture).Assembly; var resourceName = assembly.GetManifestResourceNames() .FirstOrDefault(name => name.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index ebdfdd3..9f69262 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -18,7 +18,7 @@ public class UnitTestApplication public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -37,7 +37,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var applicationManager = componentHub.ApplicationManager as ApplicationManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -57,7 +57,7 @@ public void Remove() public void Id(Type applicationType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -74,7 +74,7 @@ public void Id(Type applicationType, string id) public void Name(Type applicationType, string name) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -91,7 +91,7 @@ public void Name(Type applicationType, string name) public void Description(Type applicationType, string description) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -108,7 +108,7 @@ public void Description(Type applicationType, string description) public void Icon(Type applicationType, string icon) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -125,7 +125,7 @@ public void Icon(Type applicationType, string icon) public void ContextPath(Type applicationType, string contextPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -142,7 +142,7 @@ public void ContextPath(Type applicationType, string contextPath) public void AssetPath(Type applicationType, string assetPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -159,7 +159,7 @@ public void AssetPath(Type applicationType, string assetPath) public void DataPath(Type applicationType, string dataPath) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -173,7 +173,7 @@ public void DataPath(Type applicationType, string dataPath) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ApplicationManager.GetType())); @@ -186,7 +186,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.ApplicationManager.Applications) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs index f3a394d..2356527 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs @@ -20,7 +20,7 @@ public class UnitTestAssetManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(9, componentHub.AssetManager.Assets.Count()); @@ -33,7 +33,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var resourceManager = componentHub.AssetManager as AssetManager; @@ -59,7 +59,7 @@ public void Remove() public void Id(Type applicationType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == id); @@ -83,7 +83,7 @@ public void Id(Type applicationType, string id) public void Uri(Type applicationType, string uri) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == Path.GetFileName(uri)); @@ -116,10 +116,10 @@ public void Uri(Type applicationType, string uri) public void Request(string uri, string id, string resource) { // preconditions - var embeddedResource = UnitTestControlFixture.GetEmbeddedResource(resource); - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var context = UnitTestControlFixture.CreateHttpContextMock(); - var httpServerContext = UnitTestControlFixture.CreateHttpServerContextMock(); + var embeddedResource = UnitTestFixture.GetEmbeddedResource(resource); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestFixture.CreateHttpContextMock(); + var httpServerContext = UnitTestFixture.CreateHttpServerContextMock(); componentHub.SitemapManager.Refresh(); // test execution @@ -130,7 +130,7 @@ public void Request(string uri, string id, string resource) HttpContext = context }); - var response = componentHub.EndpointManager.HandleRequest(UnitTestControlFixture.CrerateRequestMock("", uri), searchResult.EndpointContext); + var response = componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock("", uri), searchResult.EndpointContext); Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); Assert.IsNotType(response); @@ -144,7 +144,7 @@ public void Request(string uri, string id, string resource) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.AssetManager.GetType())); @@ -157,7 +157,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var asset in componentHub.AssetManager.Assets) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs index c5caca2..d0ebaaf 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestComponentManager.cs @@ -15,7 +15,7 @@ public class UnitTestComponentManager public void PluginManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); // test execution Assert.NotNull(componentHub.PluginManager); @@ -28,7 +28,7 @@ public void PluginManager() public void ApplicationManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); // test execution Assert.NotNull(componentHub.ApplicationManager); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs index 0911bca..6972816 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs @@ -17,7 +17,7 @@ public class UnitTestEventManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(6, componentHub.EventManager.EventHandlers.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var eventManager = componentHub.EventManager as EventManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -47,7 +47,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.EventManager.GetType())); @@ -64,7 +64,7 @@ public void IsIComponentManager() public void Id(Type applicationType, Type eventType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -90,7 +90,7 @@ public void Id(Type applicationType, Type eventType, string id) public void EventId(Type applicationType, Type eventType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -112,7 +112,7 @@ public void EventId(Type applicationType, Type eventType, string id) public void RaiseEventA1() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(); // test execution @@ -130,7 +130,7 @@ public void RaiseEventA1() public void RaiseEventB1() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplication(); // test execution diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 0a4c022..72cd7f4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -18,7 +18,7 @@ public class UnitTestFragmentManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(9, componentHub.FragmentManager.Fragments.Count()); @@ -31,7 +31,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var fragmentManager = componentHub.FragmentManager as FragmentManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -48,7 +48,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.EventManager.GetType())); @@ -64,7 +64,7 @@ public void IsIComponentManager() public void Id(Type applicationType, Type fragmentType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution @@ -89,9 +89,9 @@ public void Id(Type applicationType, Type fragmentType, string id) public void Process(Type applicationType, Type sectionType, Type scopeType, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); - var renderContext = UnitTestControlFixture.CrerateRenderContextMock(application, [scopeType]); + var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); var builder = new StringBuilder(); // test execution diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs index bae1660..70664ca 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestIdentityManager.cs @@ -19,7 +19,7 @@ public class UnitTestIdentityManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(9, componentHub.IdentityManager.Permissions.Count()); @@ -33,7 +33,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var identityManager = componentHub.IdentityManager as IdentityManager; @@ -51,7 +51,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.IdentityManager.GetType())); @@ -73,7 +73,7 @@ public void IsIComponentManager() public void CheckAccessIdentity(Type application, string identityName, Type permission, bool expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); var identity = MockIdentityFactory.GetIdentity(identityName); @@ -100,7 +100,7 @@ public void CheckAccessIdentity(Type application, string identityName, Type perm public void CheckAccessGroup(Type application, string groupName, Type permission, bool expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); var group = MockIdentityFactory.GetIdentityGroup(groupName); @@ -124,7 +124,7 @@ public void CheckAccessGroup(Type application, string groupName, Type permission public void CheckAccessRole(Type application, Type role, Type permission, bool expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; var applicationContext = componentHub.ApplicationManager.GetApplications(application).FirstOrDefault(); @@ -143,9 +143,9 @@ public void CheckAccessRole(Type application, Type role, Type permission, bool e public void Login(string identityName, string password, bool expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; - var request = UnitTestControlFixture.CrerateRequestMock(); + var request = UnitTestFixture.CrerateRequestMock(); var identity = MockIdentityFactory.GetIdentity(identityName); var securePassword = new SecureString(); password.ToList().ForEach(x => securePassword.AppendChar(x)); @@ -167,9 +167,9 @@ public void Login(string identityName, string password, bool expected) public void Logout(string identityName, string password) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; - var request = UnitTestControlFixture.CrerateRequestMock(); + var request = UnitTestFixture.CrerateRequestMock(); var identity = MockIdentityFactory.GetIdentity(identityName); var securePassword = new SecureString(); password.ToList().ForEach(x => securePassword.AppendChar(x)); @@ -193,9 +193,9 @@ public void Logout(string identityName, string password) public void GetCurrentIdentity(string identityName, string password) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var identityManager = componentHub.IdentityManager as IdentityManager; - var request = UnitTestControlFixture.CrerateRequestMock(); + var request = UnitTestFixture.CrerateRequestMock(); var identity = MockIdentityFactory.GetIdentity(identityName); var securePassword = new SecureString(); password.ToList().ForEach(x => securePassword.AppendChar(x)); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 6b14444..1d64c9a 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -19,7 +19,7 @@ public class UnitTestInternationalization public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -35,7 +35,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var internationalizationManager = componentHub.InternationalizationManager as InternationalizationManager; var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -52,7 +52,7 @@ public void Remove() public void GetDefaultCulture() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(CultureInfo.GetCultureInfo("en"), InternationalizationManager.DefaultCulture); @@ -72,7 +72,7 @@ public void GetDefaultCulture() public void Translate(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { // preconditions - UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + UnitTestFixture.CreateAndRegisterComponentHubMock(); if (cultureName == null && !param.Any()) { @@ -126,7 +126,7 @@ public void Translate(string key, string excepted, string cultureName = null, st public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.InternationalizationManager.GetType())); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs index 950765b..9864ba4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs @@ -17,7 +17,7 @@ public class UnitTestJobManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(3, componentHub.JobManager.Jobs.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var jobManager = componentHub.JobManager as JobManager; @@ -47,7 +47,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); @@ -60,7 +60,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var job in componentHub.JobManager.Jobs) @@ -80,7 +80,7 @@ public void IsIContext() public void Id(Type applicationType, Type jobType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var job = componentHub.JobManager.GetJob(application, jobType); @@ -98,7 +98,7 @@ public void Id(Type applicationType, Type jobType, string id) public void Cron(Type applicationType, Type jobType, int minute, int hour, int day, int[] month, int weekday) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var job = componentHub.JobManager.GetJob(application, jobType); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs index 988299c..96f8c3d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestLogManager.cs @@ -17,7 +17,7 @@ public class UnitTestLogManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var logManager = componentHub.LogManager as LogManager; // test execution @@ -31,7 +31,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var logManager = componentHub.LogManager as LogManager; // test execution @@ -45,7 +45,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var logManager = componentHub.LogManager as LogManager; // test execution diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs index 242c2ce..dd8c04c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPackageManager.cs @@ -17,7 +17,7 @@ public class UnitTestPackageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var packageManager = componentHub.PackageManager as PackageManager; // test execution @@ -31,7 +31,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var packageManager = componentHub.PackageManager as PackageManager; // test execution @@ -45,7 +45,7 @@ public void Remove() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var packageManager = componentHub.PackageManager as PackageManager; // test execution diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index 057855e..33a9b6d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -17,7 +17,7 @@ public class UnitTestPageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(9, componentHub.PageManager.Pages.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var pageManager = componentHub.PageManager as PageManager; @@ -56,7 +56,7 @@ public void Remove() public void Id(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); @@ -81,7 +81,7 @@ public void Id(Type applicationType, Type resourceType, string id) public void Title(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); @@ -105,7 +105,7 @@ public void Title(Type applicationType, Type resourceType, string id) public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); @@ -120,7 +120,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.PageManager.GetType())); @@ -133,7 +133,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var pages in componentHub.PageManager.Pages) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs index 43d4534..46aa54c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs @@ -17,7 +17,7 @@ public class UnitTestPluginManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -34,7 +34,7 @@ public void Register() public void RegisterEvent() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; var i = 0; var triggered = false; @@ -57,7 +57,7 @@ public void RegisterEvent() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -75,7 +75,7 @@ public void Remove() public void RemoveEvent() { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; var i = 1; var triggered = false; @@ -99,7 +99,7 @@ public void RemoveEvent() public void GetPluginById() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution var plugin = componentHub.PluginManager.GetPlugin("webexpress.webcore.test"); @@ -114,7 +114,7 @@ public void GetPluginById() public void GetPluginByType() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); @@ -129,7 +129,7 @@ public void GetPluginByType() public void Id() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -143,7 +143,7 @@ public void Id() public void GetName() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -157,7 +157,7 @@ public void GetName() public void GetDescription() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -171,7 +171,7 @@ public void GetDescription() public void GetIcon() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution @@ -189,7 +189,7 @@ public void GetIcon() public void Boot(string pluginId, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(pluginId); @@ -212,7 +212,7 @@ public void Boot(string pluginId, string expected) public void ShutDown(string pluginId, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateComponentHubMock(); + var componentHub = UnitTestFixture.CreateComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); var plugin = componentHub.PluginManager.GetPlugin(pluginId); @@ -231,7 +231,7 @@ public void ShutDown(string pluginId, string expected) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var pluginManager = componentHub.PluginManager as PluginManager; // test execution @@ -245,7 +245,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var plugin in componentHub.PluginManager.Plugins) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index 23304aa..fa0c361 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -17,7 +17,7 @@ public class UnitTestResourceManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(12, componentHub.ResourceManager.Resources.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var resourceManager = componentHub.ResourceManager as ResourceManager; @@ -59,7 +59,7 @@ public void Remove() public void Id(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); @@ -87,7 +87,7 @@ public void Id(Type applicationType, Type resourceType, string id) public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); @@ -102,7 +102,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); @@ -115,7 +115,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var resources in componentHub.ResourceManager.Resources) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 86d533d..101d421 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -17,7 +17,7 @@ public class UnitTestRestApiManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(9, componentHub.RestApiManager.RestApis.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var apiManager = componentHub.RestApiManager as RestApiManager; @@ -56,7 +56,7 @@ public void Remove() public void Id(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); @@ -80,7 +80,7 @@ public void Id(Type applicationType, Type resourceType, string id) public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); @@ -99,7 +99,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) public void Method(Type applicationType, Type resourceType, CrudMethod method) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); @@ -114,7 +114,7 @@ public void Method(Type applicationType, Type resourceType, CrudMethod method) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.RestApiManager.GetType())); @@ -127,7 +127,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var api in componentHub.RestApiManager.RestApis) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs index fc00c0c..e7bf41e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSessionManager.cs @@ -18,7 +18,7 @@ public class UnitTestSessionManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.NotNull(componentHub.SessionManager); @@ -31,7 +31,7 @@ public void Register() public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SessionManager.GetType())); @@ -44,8 +44,8 @@ public void IsIComponentManager() public void GetSession() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var request = UnitTestControlFixture.CrerateRequestMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestFixture.CrerateRequestMock(); // test execution var session = componentHub.SessionManager.GetSession(request); @@ -60,8 +60,8 @@ public void GetSession() public void AddPropertyToSession() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var request = UnitTestControlFixture.CrerateRequestMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestFixture.CrerateRequestMock(); var session = componentHub.SessionManager.GetSession(request); // test execution @@ -82,8 +82,8 @@ public void AddPropertyToSession() public void RemovePropertyFromSession() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var request = UnitTestControlFixture.CrerateRequestMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var request = UnitTestFixture.CrerateRequestMock(); var session = componentHub.SessionManager.GetSession(request); // test execution diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 224ee04..1189d71 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -17,7 +17,7 @@ public class UnitTestSettingPageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(6, componentHub.SettingPageManager.SettingPages.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var settingPageManager = componentHub.SettingPageManager as SettingPageManager; @@ -53,7 +53,7 @@ public void Remove() public void Id(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); @@ -75,7 +75,7 @@ public void Id(Type applicationType, Type resourceType, string id) public void Title(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); @@ -96,7 +96,7 @@ public void Title(Type applicationType, Type resourceType, string id) public void ContextPath(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); @@ -111,7 +111,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SettingPageManager.GetType())); @@ -124,7 +124,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var settingPages in componentHub.SettingPageManager.SettingPages) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 391680e..65b4d54 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -17,7 +17,7 @@ public class UnitTestSitemapManager public void Refresh() { // preconditions - var componentManager = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentManager = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution componentManager.SitemapManager.Refresh(); @@ -65,9 +65,9 @@ public void Refresh() public void SearchResource(string uri, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); - var context = UnitTestControlFixture.CreateHttpContextMock(); - var httpServerContext = UnitTestControlFixture.CreateHttpServerContextMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var context = UnitTestFixture.CreateHttpContextMock(); + var httpServerContext = UnitTestFixture.CreateHttpServerContextMock(); componentHub.SitemapManager.Refresh(); // test execution @@ -78,7 +78,7 @@ public void SearchResource(string uri, string id) HttpContext = context }); - componentHub.EndpointManager.HandleRequest(UnitTestControlFixture.CrerateRequestMock(), searchResult.EndpointContext); + componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock(), searchResult.EndpointContext); Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); } @@ -97,7 +97,7 @@ public void SearchResource(string uri, string id) public void GetUri(Type resourceType, string expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); componentHub.SitemapManager.Refresh(); // test execution @@ -113,7 +113,7 @@ public void GetUri(Type resourceType, string expected) public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SitemapManager.GetType())); diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index dc9fcdc..0191fe0 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -17,7 +17,7 @@ public class UnitTestStatusPageManager public void Register() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.Equal(12, componentHub.StatusPageManager.StatusPages.Count()); @@ -30,7 +30,7 @@ public void Register() public void Remove() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); var statusPageManager = componentHub.StatusPageManager as StatusPageManager; @@ -52,7 +52,7 @@ public void Remove() public void Id(Type applicationType, Type statusPageType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -79,7 +79,7 @@ public void Id(Type applicationType, Type statusPageType, string id) public void Title(Type applicationType, Type resourceType, string id) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, resourceType); @@ -106,7 +106,7 @@ public void Title(Type applicationType, Type resourceType, string id) public void Code(Type applicationType, Type statusPageType, int? code) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -133,7 +133,7 @@ public void Code(Type applicationType, Type statusPageType, int? code) public void Icon(Type applicationType, Type statusPageType, string icon) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); @@ -153,9 +153,9 @@ public void Icon(Type applicationType, Type statusPageType, string icon) public void CreateAndCheckCode(Type applicationType, int statusCode, int? expected) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); - var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse("content", statusCode, application, UnitTestFixture.CreateHttpContextMock().Request); // test execution Assert.Equal(expected, statusResponse?.Status); @@ -170,9 +170,9 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected, int length) { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); - var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestControlFixture.CreateHttpContextMock().Request); + var statusResponse = componentHub.StatusPageManager.CreateStatusResponse(content, statusCode, application, UnitTestFixture.CreateHttpContextMock().Request); // test execution Assert.Contains(expected, statusResponse?.Content?.ToString()); @@ -186,7 +186,7 @@ public void CreateAndCheckMessage(Type applicationType, int statusCode, string c public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.StatusPageManager.GetType())); @@ -199,7 +199,7 @@ public void IsIComponentManager() public void IsIContext() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution foreach (var application in componentHub.StatusPageManager.StatusPages) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs index 1533beb..cc5b0db 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestTaskManager.cs @@ -16,7 +16,7 @@ public class UnitTestTaskManager public void IsIComponentManager() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ResourceManager.GetType())); @@ -29,7 +29,7 @@ public void IsIComponentManager() public void IsCompopnent() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); componentHub.TaskManager.CreateTask("test"); @@ -47,7 +47,7 @@ public void IsCompopnent() public void CreateSystemTask() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution var task = componentHub.TaskManager.CreateTask("test"); @@ -61,7 +61,7 @@ public void CreateSystemTask() public void CreateOwnTask() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution var task = componentHub.TaskManager.CreateTask("test", null, []); @@ -75,7 +75,7 @@ public void CreateOwnTask() public void ContainsTask() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var task = componentHub.TaskManager.CreateTask("test"); // test execution @@ -90,7 +90,7 @@ public void ContainsTask() public void GetTask() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var task = componentHub.TaskManager.CreateTask("test"); // test execution @@ -105,7 +105,7 @@ public void GetTask() public void RemoveTask() { // preconditions - var componentHub = UnitTestControlFixture.CreateAndRegisterComponentHubMock(); + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var task = componentHub.TaskManager.CreateTask("test"); // test execution diff --git a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs index eb79278..a7d6814 100644 --- a/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs +++ b/src/WebExpress.WebCore.Test/Message/UnitTestGetRequest.cs @@ -14,8 +14,8 @@ public class UnitTestGetRequest [Fact] public void General() { - var content = UnitTestControlFixture.GetEmbeddedResource("general.get"); - var request = UnitTestControlFixture.CrerateRequestMock(content); + var content = UnitTestFixture.GetEmbeddedResource("general.get"); + var request = UnitTestFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -26,8 +26,8 @@ public void General() [Fact] public void Less() { - var content = UnitTestControlFixture.GetEmbeddedResource("less.get"); - var request = UnitTestControlFixture.CrerateRequestMock(content); + var content = UnitTestFixture.GetEmbeddedResource("less.get"); + var request = UnitTestFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -38,8 +38,8 @@ public void Less() [Fact] public void Massive() { - var content = UnitTestControlFixture.GetEmbeddedResource("massive.get"); - var request = UnitTestControlFixture.CrerateRequestMock(content); + var content = UnitTestFixture.GetEmbeddedResource("massive.get"); + var request = UnitTestFixture.CrerateRequestMock(content); Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); } @@ -50,8 +50,8 @@ public void Massive() [Fact] public void GetParameter() { - var content = UnitTestControlFixture.GetEmbeddedResource("param.get"); - var request = UnitTestControlFixture.CrerateRequestMock(content); + var content = UnitTestFixture.GetEmbeddedResource("param.get"); + var request = UnitTestFixture.CrerateRequestMock(content); var param = request?.GetParameter("a")?.Value; Assert.Equal("http://localhost:8080/abc/xyz/A7BCCCA9-4C7E-4117-9EE2-ECC3381B605A", request.Uri?.ToString()); @@ -64,8 +64,8 @@ public void GetParameter() [Fact] public void GetParameterWithUmlaut() { - var content = UnitTestControlFixture.GetEmbeddedResource("param_umlaut.get"); - var request = UnitTestControlFixture.CrerateRequestMock(content); + var content = UnitTestFixture.GetEmbeddedResource("param_umlaut.get"); + var request = UnitTestFixture.CrerateRequestMock(content); var a = request?.GetParameter("a")?.Value; var b = request?.GetParameter("b")?.Value; diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs index 3988fce..65a1400 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestCron.cs @@ -14,7 +14,7 @@ public class UnitTestCron public void Create_1() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var clock = new Clock(); var cron = new Cron(context, "0-59", "*", "1-31", "1-2,3,4,5,6,7,8-10,11,12"); @@ -26,7 +26,7 @@ public void Create_1() public void Create_2() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 1, dateTime.Day, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "0-33", "2, 1-4, x"); @@ -39,7 +39,7 @@ public void Create_2() public void Create_3() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "12"); @@ -52,7 +52,7 @@ public void Create_3() public void Create_4() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; Log.Current.Clear(); @@ -67,7 +67,7 @@ public void Create_4() public void Create_5() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "*", ""); @@ -80,7 +80,7 @@ public void Create_5() public void Create_6() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; Log.Current.Clear(); @@ -95,7 +95,7 @@ public void Create_6() public void Matching_1() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(dateTime.Year, 12, 31, dateTime.Hour, dateTime.Minute, 0)); var cron = new Cron(context, "*", "*", "31", "1-11"); @@ -108,7 +108,7 @@ public void Matching_1() public void Matching_2() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "3"); // wednesday @@ -121,7 +121,7 @@ public void Matching_2() public void Matching_3() { // preconditions - var context = UnitTestControlFixture.CreateHttpServerContextMock(); + var context = UnitTestFixture.CreateHttpServerContextMock(); var dateTime = DateTime.Now; var clock = new Clock(new DateTime(2020, 1, 1, dateTime.Hour, dateTime.Minute, 0)); // wednesday var cron = new Cron(context, "*", "*", "*", "*", "1"); // sunday diff --git a/src/WebExpress.WebCore.Test/TestPageA.cs b/src/WebExpress.WebCore.Test/TestPageA.cs index 45dd6c8..6c5c918 100644 --- a/src/WebExpress.WebCore.Test/TestPageA.cs +++ b/src/WebExpress.WebCore.Test/TestPageA.cs @@ -36,15 +36,6 @@ public TestPageA(IPageContext pageContext) } } - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { - - } - /// /// Processing of the page. /// diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index d963c30..a3d29ef 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -12,13 +12,6 @@ public interface IPage : IEndpoint /// /// The context for rendering the page. void Process(IRenderContext context); - - /// - /// Redirect to another page. - /// The function throws the RedirectException. - /// - /// The uri to redirect to. - void Redirecting(string uri); } /// From 7c8809dff3240f39175d17d0455b84cfcf21281b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 7 Dec 2024 10:37:40 +0100 Subject: [PATCH 049/162] add IComponentId --- .../Manager/UnitTestAssetManager.cs | 8 +- .../Manager/UnitTestEventManager.cs | 2 +- .../Manager/UnitTestFragmentManager.cs | 2 +- .../Manager/UnitTestJobManager.cs | 2 +- .../Manager/UnitTestPageManager.cs | 2 +- .../Manager/UnitTestPluginManager.cs | 14 +- .../Manager/UnitTestResourceManager.cs | 2 +- .../Manager/UnitTestRestApiManager.cs | 2 +- .../Manager/UnitTestSettingPageManager.cs | 2 +- .../Manager/UnitTestSitemapManager.cs | 2 +- .../Manager/UnitTestStatusPageManager.cs | 2 +- .../TestApplicationA.cs | 8 - src/WebExpress.WebCore.Test/TestFragmentA.cs | 13 +- .../InternationalizationExtensions.cs | 46 ------ .../InternationalizationManager.cs | 2 +- src/WebExpress.WebCore/WebAsset/Asset.cs | 2 +- .../WebAsset/AssetContext.cs | 3 +- .../WebAsset/AssetManager.cs | 8 +- .../WebComponent/ComponentActivator.cs | 18 ++- .../WebComponent/ComponentId.cs | 143 ++++++++++++++++++ .../WebComponent/IComponentId.cs | 9 ++ .../WebEndpoint/IEndpointContext.cs | 2 +- .../WebEvent/EventHandlerContext.cs | 3 +- .../WebEvent/EventManager.cs | 2 +- .../WebEvent/IEventHandlerContext.cs | 2 +- .../WebFragment/FragmentContext.cs | 3 +- .../WebFragment/FragmentManager.cs | 2 +- .../WebFragment/IFragmentContext.cs | 2 +- .../WebIdentity/IIdentityPermissionContext.cs | 2 +- .../WebIdentity/IIdentityRoleContext.cs | 2 +- .../WebIdentity/IdentityManager.cs | 4 +- .../WebIdentity/IdentityPermissionContext.cs | 3 +- .../WebIdentity/IdentityRoleContext.cs | 3 +- src/WebExpress.WebCore/WebJob/IJobContext.cs | 2 +- src/WebExpress.WebCore/WebJob/JobContext.cs | 3 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 4 +- src/WebExpress.WebCore/WebPage/PageContext.cs | 3 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 2 +- .../WebPlugin/IPluginContext.cs | 2 +- .../WebPlugin/Model/PluginDictionary.cs | 3 +- .../WebPlugin/PluginContext.cs | 5 +- .../WebPlugin/PluginManager.cs | 16 +- .../WebResource/ResourceContext.cs | 3 +- .../WebResource/ResourceManager.cs | 2 +- .../WebRestAPI/RestApiContext.cs | 3 +- .../WebRestAPI/RestApiManager.cs | 2 +- .../Model/SettingPageDictionaryItemGroup.cs | 2 +- .../WebSettingPage/SettingPageContext.cs | 3 +- .../WebSettingPage/SettingPageManager.cs | 2 +- .../WebSitemap/SitemapManager.cs | 4 +- .../WebStatusPage/IStatusPageContext.cs | 10 +- .../WebStatusPage/Model/StatusPageItem.cs | 7 +- .../WebStatusPage/StatusPageContext.cs | 7 +- .../WebStatusPage/StatusPageManager.cs | 12 +- 54 files changed, 268 insertions(+), 151 deletions(-) delete mode 100644 src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs create mode 100644 src/WebExpress.WebCore/WebComponent/ComponentId.cs create mode 100644 src/WebExpress.WebCore/WebComponent/IComponentId.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs index 2356527..9f8a9df 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs @@ -61,10 +61,10 @@ public void Id(Type applicationType, string id) // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == id); + var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId.ToString() == id); // test execution - Assert.Equal(id, asset?.EndpointId); + Assert.Equal(id, asset?.EndpointId.ToString()); } /// @@ -85,7 +85,7 @@ public void Uri(Type applicationType, string uri) // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId == Path.GetFileName(uri)); + var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId.ToString() == Path.GetFileName(uri)); // test execution Assert.Equal(uri, asset?.Uri); @@ -132,7 +132,7 @@ public void Request(string uri, string id, string resource) var response = componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock("", uri), searchResult.EndpointContext); - Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); + Assert.Equal(id, searchResult?.EndpointContext?.EndpointId.ToString()); Assert.IsNotType(response); Assert.Equal(embeddedResource, Encoding.UTF8.GetString(response.Content as byte[])); } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs index 6972816..9c68326 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestEventManager.cs @@ -102,7 +102,7 @@ public void EventId(Type applicationType, Type eventType, string id) return; } - Assert.Contains(id, eventHandlers?.Select(x => x.EventId)); + Assert.Contains(id, eventHandlers?.Select(x => x.EventId.ToString())); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 72cd7f4..1c33b16 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -76,7 +76,7 @@ public void Id(Type applicationType, Type fragmentType, string id) return; } - Assert.Contains(id, fragment.Select(x => x.FragmentId)); + Assert.Contains(id, fragment.Select(x => x.FragmentId?.ToString())); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs index 9864ba4..3f4eb85 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestJobManager.cs @@ -85,7 +85,7 @@ public void Id(Type applicationType, Type jobType, string id) var job = componentHub.JobManager.GetJob(application, jobType); // test execution - Assert.Equal(id, job?.JobId); + Assert.Equal(id, job?.JobId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index 33a9b6d..dc38a99 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -61,7 +61,7 @@ public void Id(Type applicationType, Type resourceType, string id) var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, page.EndpointId); + Assert.Equal(id, page.EndpointId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs index 46aa54c..ae04338 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs @@ -24,7 +24,7 @@ public void Register() pluginManager.Register(); Assert.Single(componentHub.PluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); + Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId.ToString()); } /// @@ -45,7 +45,7 @@ public void RegisterEvent() pluginManager.Register(); Assert.Single(componentHub.PluginManager.Plugins); - Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId); + Assert.Contains("webexpress.webcore.test", componentHub.PluginManager.GetPlugin(typeof(TestPlugin))?.PluginId.ToString()); Assert.Equal(1, i); Assert.True(triggered); } @@ -104,7 +104,7 @@ public void GetPluginById() // test execution var plugin = componentHub.PluginManager.GetPlugin("webexpress.webcore.test"); - Assert.Equal("webexpress.webcore.test", plugin?.PluginId); + Assert.Equal("webexpress.webcore.test", plugin?.PluginId.ToString()); } /// @@ -119,7 +119,7 @@ public void GetPluginByType() // test execution var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); - Assert.Equal("webexpress.webcore.test", plugin?.PluginId); + Assert.Equal("webexpress.webcore.test", plugin?.PluginId.ToString()); } /// @@ -133,7 +133,7 @@ public void Id() var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - Assert.Equal(typeof(TestPlugin).Namespace.ToLower(), plugin.PluginId); + Assert.Equal(typeof(TestPlugin).Namespace.ToLower(), plugin.PluginId.ToString()); } /// @@ -198,7 +198,7 @@ public void Boot(string pluginId, string expected) pluginManager.Boot(plugin); Assert.Single(componentHub.PluginManager.Plugins); - Assert.Equal(expected, plugin?.PluginId); + Assert.Equal(expected, plugin?.PluginId.ToString()); } /// @@ -221,7 +221,7 @@ public void ShutDown(string pluginId, string expected) pluginManager.ShutDown(plugin); Assert.Single(componentHub.PluginManager.Plugins); - Assert.Equal(expected, plugin?.PluginId); + Assert.Equal(expected, plugin?.PluginId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index fa0c361..b8b97fe 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -64,7 +64,7 @@ public void Id(Type applicationType, Type resourceType, string id) var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, resource?.EndpointId); + Assert.Equal(id, resource?.EndpointId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 101d421..b34fa24 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -61,7 +61,7 @@ public void Id(Type applicationType, Type resourceType, string id) var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, api?.EndpointId); + Assert.Equal(id, api?.EndpointId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 1189d71..43ac5c4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -58,7 +58,7 @@ public void Id(Type applicationType, Type resourceType, string id) var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, settingPage.EndpointId); + Assert.Equal(id, settingPage.EndpointId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 65b4d54..3005a14 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -80,7 +80,7 @@ public void SearchResource(string uri, string id) componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock(), searchResult.EndpointContext); - Assert.Equal(id, searchResult?.EndpointContext?.EndpointId); + Assert.Equal(id, searchResult?.EndpointContext?.EndpointId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 0191fe0..5b4c089 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -57,7 +57,7 @@ public void Id(Type applicationType, Type statusPageType, string id) var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); // test execution - Assert.Equal(id, statusPage.StatusId); + Assert.Equal(id, statusPage.StatusPageId.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/TestApplicationA.cs b/src/WebExpress.WebCore.Test/TestApplicationA.cs index f0b51c0..5b687a7 100644 --- a/src/WebExpress.WebCore.Test/TestApplicationA.cs +++ b/src/WebExpress.WebCore.Test/TestApplicationA.cs @@ -28,14 +28,6 @@ private TestApplicationA(IApplicationContext applicationContext) } } - /// - /// Initialization of the application. - /// - /// The application context. - public void Initialization(IApplicationContext applicationContext) - { - } - /// /// Called when the plugin starts working. The call is concurrent. /// diff --git a/src/WebExpress.WebCore.Test/TestFragmentA.cs b/src/WebExpress.WebCore.Test/TestFragmentA.cs index 73a30a3..e6060b4 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentA.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentA.cs @@ -20,18 +20,25 @@ public sealed class TestFragmentA : IFragment /// /// The component hub. /// The context of the fragment. - public TestFragmentA(IComponentHub componentHub, IFragmentContext fragmentContext) + /// The unique identifier for the fragment. + public TestFragmentA(IComponentHub componentHub, IFragmentContext fragmentContext, IComponentId id) { // test the injection if (componentHub == null) { - throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(componentHub), "Parameter componentHub cannot be null or empty."); } // test the injection if (fragmentContext == null) { - throw new ArgumentNullException(nameof(fragmentContext), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(fragmentContext), "Parameter fragmentContext cannot be null or empty."); + } + + // test the injection + if (string.IsNullOrWhiteSpace(id?.ToString())) + { + throw new ArgumentNullException(nameof(fragmentContext), "Parameter id cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs deleted file mode 100644 index 9d2e010..0000000 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebPage; - -namespace WebExpress.WebCore.Internationalization -{ - /// - /// Provides extension methods for internationalization. - /// - public static class InternationalizationExtensions - { - /// - /// Internationalization of a key. - /// - /// An render context object that is being extended. - /// The plugin id. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this RenderContext obj, string pluginId, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, pluginId, key); - } - - /// - /// Internationalization of a key. - /// - /// An render context object that is being extended. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this RenderContext obj, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, obj?.PageContext?.PluginContext?.PluginId, key); - } - - /// - /// Internationalization of a key. - /// - /// An render context object that is being extended. - /// The application context. - /// The internationalization key. - /// The value of the key in the current language. - public static string I18N(this RenderContext obj, IApplicationContext applicationContext, string key) - { - return WebEx.ComponentHub?.InternationalizationManager.Translate(obj.Culture, applicationContext?.PluginContext?.PluginId, key); - } - } -} diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 4916a95..56d5eb4 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -67,7 +67,7 @@ private InternationalizationManager(IComponentHub componentHub, IHttpServerConte public void Register(IPluginContext pluginContext) { var pluginId = pluginContext.PluginId; - Register(pluginContext.Assembly, pluginId); + Register(pluginContext.Assembly, pluginId.ToString()); HttpServerContext.Log.Debug ( diff --git a/src/WebExpress.WebCore/WebAsset/Asset.cs b/src/WebExpress.WebCore/WebAsset/Asset.cs index 2102a96..f119f6e 100644 --- a/src/WebExpress.WebCore/WebAsset/Asset.cs +++ b/src/WebExpress.WebCore/WebAsset/Asset.cs @@ -54,7 +54,7 @@ public Response Process(Request request) return new ResponseNotFound(); } - var extension = Path.GetExtension(_assetContext.EndpointId)?.ToLower() ?? ""; + var extension = Path.GetExtension(_assetContext.EndpointId.ToString())?.ToLower() ?? ""; var response = new ResponseOK(); response.Header.CacheControl = "public, max-age=31536000"; diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs index 5caca6c..2a450e0 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetContext.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; @@ -33,7 +34,7 @@ public class AssetContext : IAssetContext /// /// Returns the resource id. /// - public string EndpointId { get; internal set; } + public IComponentId EndpointId { get; internal set; } /// /// Returns the parent or null if not used. diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 5ee8e5f..c236483 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -71,7 +71,7 @@ private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerCo var asset = _itemDictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x) - .FirstOrDefault(x => request.Uri.ToString().ToLower().Replace('/', '.').EndsWith(x.AssetContext.EndpointId)); + .FirstOrDefault(x => request.Uri.ToString().ToLower().Replace('/', '.').EndsWith(x.AssetContext.EndpointId.ToString())); if (asset != null) { @@ -148,7 +148,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(Type componentType, C context, IHttpServerC { // injection var parameters = constructor.GetParameters(); - var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var hubProperties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var contextIdProperty = context.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.PropertyType == typeof(IComponentId)) + .FirstOrDefault(); var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : parameter.ParameterType == typeof(C) ? context : - properties.Where(x => x.PropertyType == parameter.ParameterType) - .FirstOrDefault()? - .GetValue(componentHub) ?? + parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(context) : + hubProperties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? advancedParameters.Where(x => x.GetType() == parameter.ParameterType) - .FirstOrDefault() ?? null + .FirstOrDefault() ?? null ).ToArray(); if (constructor.Invoke(parameterValues) is T component) @@ -250,11 +254,15 @@ public static IComponent CreateInstance(Type componentType, C context, IHttpS // injection var parameters = constructor.GetParameters(); var properties = componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var contextIdProperty = context.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.PropertyType == typeof(IComponentId)) + .FirstOrDefault(); var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : parameter.ParameterType == typeof(C) ? context : + parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(context) : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? .GetValue(componentHub) ?? diff --git a/src/WebExpress.WebCore/WebComponent/ComponentId.cs b/src/WebExpress.WebCore/WebComponent/ComponentId.cs new file mode 100644 index 0000000..57bd4fc --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/ComponentId.cs @@ -0,0 +1,143 @@ +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Represents a component identifier. + /// + public class ComponentId : IComponentId + { + private readonly string _id; + + /// + /// Initializes a new instance of the class with the specified identifier. + /// + /// The identifier of the component. + public ComponentId(string id) + { + _id = id?.ToLower(); + } + + /// + /// Defines an implicit conversion of a string to a . + /// + /// The identifier of the component. + /// A new instance of initialized with the specified identifier. + public static implicit operator ComponentId(string id) + { + return new ComponentId(id); + } + + /// + /// Defines an explicit conversion of a to a string. + /// + /// The to convert. + /// The identifier of the component as a string. + public static explicit operator string(ComponentId componentId) + { + return componentId._id; + } + + /// + /// Determines whether two specified objects have the same value. + /// + /// The first to compare. + /// The second to compare. + /// true if the value of is the same as the value of ; otherwise, false. + public static bool operator ==(ComponentId lhs, IComponentId rhs) + { + if (ReferenceEquals(lhs, rhs)) + { + return true; + } + if (lhs is null || rhs is null) + { + return false; + } + + return lhs.ToString().Equals(rhs.ToString()); + } + + /// + /// Determines whether two specified objects have the same value. + /// + /// The first to compare. + /// The second string to compare. + /// true if the value of is the same as the value of ; otherwise, false. + public static bool operator ==(ComponentId lhs, string rhs) + { + if (ReferenceEquals(lhs, rhs)) + { + return true; + } + if (lhs is null || rhs is null) + { + return false; + } + + return lhs._id == rhs; + } + + /// + /// Determines whether two specified objects have different values. + /// + /// The first to compare. + /// The second to compare. + /// true if the value of is different from the value of ; otherwise, false. + public static bool operator !=(ComponentId lhs, IComponentId rhs) + { + return !(lhs == rhs); + } + + /// + /// Determines whether two specified objects have different values. + /// + /// The first to compare. + /// The second string to compare. + /// true if the value of is different from the value of ; otherwise, false. + public static bool operator !=(ComponentId lhs, string rhs) + { + return !(lhs == rhs); + } + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// true if the specified object is equal to the current ; otherwise, false. + public bool Equals(string obj) + { + return _id.Equals(obj); + } + + /// + /// Determines whether the specified object is equal to the current . + /// + /// The object to compare with the current . + /// true if the specified object is equal to the current ; otherwise, false. + public override bool Equals(object obj) + { + if (obj is ComponentId other) + { + return _id == other._id; + } + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current . + public override int GetHashCode() + { + return _id.GetHashCode(); + } + + /// + /// Returns the string representation of the component identifier. + /// + /// The identifier of the component as a string. + public override string ToString() + { + return _id; + } + } +} diff --git a/src/WebExpress.WebCore/WebComponent/IComponentId.cs b/src/WebExpress.WebCore/WebComponent/IComponentId.cs new file mode 100644 index 0000000..e4229d0 --- /dev/null +++ b/src/WebExpress.WebCore/WebComponent/IComponentId.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebComponent +{ + /// + /// Represents an interface for component identifiers. + /// + public interface IComponentId + { + } +} diff --git a/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs index 1feca8d..6ba82c8 100644 --- a/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs @@ -15,7 +15,7 @@ public interface IEndpointContext : IContext /// /// Returns the endpoint id. /// - string EndpointId { get; } + IComponentId EndpointId { get; } /// /// Returns the associated plugin context. diff --git a/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs index f72260e..9191a50 100644 --- a/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs +++ b/src/WebExpress.WebCore/WebEvent/EventHandlerContext.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebEvent @@ -11,7 +12,7 @@ public class EventHandlerContext : IEventHandlerContext /// /// Returns the event id. /// - public string EventId { get; internal set; } + public IComponentId EventId { get; internal set; } /// /// Returns the event handler id. diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 954183e..83b7cc8 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -186,7 +186,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Returns the event id. /// - string EventId { get; } + IComponentId EventId { get; } /// /// Returns the event handler id. diff --git a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs index 021cca0..5350721 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebPlugin; @@ -25,7 +26,7 @@ public class FragmentContext : IFragmentContext /// /// Gets the unique identifier for the fragment. /// - public string FragmentId { get; internal set; } + public IComponentId FragmentId { get; internal set; } /// /// Returns the conditions that must be met for the component to be active. diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index c44c87a..ba3d585 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -189,7 +189,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Gets the unique identifier for the fragment. /// - string FragmentId { get; } + IComponentId FragmentId { get; } /// /// Returns the conditions that must be met for the component to be active. diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs index 9ab9e73..870b8a1 100644 --- a/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityPermissionContext.cs @@ -13,7 +13,7 @@ public interface IIdentityPermissionContext : IContext /// /// Returns the permission id. /// - string PermissionId { get; } + IComponentId PermissionId { get; } /// /// Returns the permission. diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs index ac9657b..08fea06 100644 --- a/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityRoleContext.cs @@ -12,7 +12,7 @@ public interface IIdentityRoleContext : IContext /// /// Returns the role id. /// - string RoleId { get; } + IComponentId RoleId { get; } /// /// Returns the associated plugin context. diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs index 37c8934..ecb8203 100644 --- a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs +++ b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs @@ -128,7 +128,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); foreach (var customAttribute in permissionType.CustomAttributes @@ -198,7 +198,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); foreach (var customAttribute in roleType.CustomAttributes diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs b/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs index 43e6a70..f7daaf3 100644 --- a/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs +++ b/src/WebExpress.WebCore/WebIdentity/IdentityPermissionContext.cs @@ -1,5 +1,6 @@ using System; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebIdentity @@ -12,7 +13,7 @@ public class IdentityPermissionContext : IIdentityPermissionContext /// /// Returns the permission id. /// - public string PermissionId { get; internal set; } + public IComponentId PermissionId { get; internal set; } /// /// Returns the permission. diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs b/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs index b380a67..47683e4 100644 --- a/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs +++ b/src/WebExpress.WebCore/WebIdentity/IdentityRoleContext.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebIdentity @@ -11,7 +12,7 @@ public class IdentityRoleContext : IIdentityRoleContext /// /// Returns the role id. /// - public string RoleId { get; internal set; } + public IComponentId RoleId { get; internal set; } /// /// Returns the associated plugin context. diff --git a/src/WebExpress.WebCore/WebJob/IJobContext.cs b/src/WebExpress.WebCore/WebJob/IJobContext.cs index 34adba6..ae45b1d 100644 --- a/src/WebExpress.WebCore/WebJob/IJobContext.cs +++ b/src/WebExpress.WebCore/WebJob/IJobContext.cs @@ -22,7 +22,7 @@ public interface IJobContext : IContext /// /// Returns the job id. /// - string JobId { get; } + IComponentId JobId { get; } /// /// Returns the cron-object. diff --git a/src/WebExpress.WebCore/WebJob/JobContext.cs b/src/WebExpress.WebCore/WebJob/JobContext.cs index 08905c4..7f120d9 100644 --- a/src/WebExpress.WebCore/WebJob/JobContext.cs +++ b/src/WebExpress.WebCore/WebJob/JobContext.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebJob @@ -21,7 +22,7 @@ public class JobContext : IJobContext /// /// Returns the job id. /// - public string JobId { get; internal set; } + public IComponentId JobId { get; internal set; } /// /// Returns the cron-object. diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 07046f2..cb4095d 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -80,7 +80,7 @@ private IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob var jobContext = new JobContext() { PluginContext = pluginContext, - JobId = typeof(T).FullName?.ToLower(), + JobId = new ComponentId(typeof(T).FullName), Cron = cron }; @@ -163,7 +163,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Returns the endpoint id. /// - public string EndpointId { get; internal set; } + public IComponentId EndpointId { get; internal set; } /// /// Returns the resource title. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index a870d51..b6b067d 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -343,7 +343,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Returns the plugin id. /// - string PluginId { get; } + IComponentId PluginId { get; } /// /// Returns the name of the plugin. diff --git a/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs b/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs index f91e6f7..fd436af 100644 --- a/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs +++ b/src/WebExpress.WebCore/WebPlugin/Model/PluginDictionary.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPlugin.Model { @@ -7,7 +8,7 @@ namespace WebExpress.WebCore.WebPlugin.Model /// Key = PluginId /// Value = Plugin-Metadaten /// - internal class PluginDictionary : Dictionary + internal class PluginDictionary : Dictionary { } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs index cb4783c..1228871 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs @@ -1,4 +1,5 @@ using System.Reflection; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPlugin @@ -13,7 +14,7 @@ public class PluginContext : IPluginContext /// /// Returns the plugin id. /// - public string PluginId { get; internal set; } + public IComponentId PluginId { get; internal set; } /// /// Returns the name of the plugin. @@ -68,7 +69,7 @@ public PluginContext() /// The string that uniquely represents the plugin. public override string ToString() { - return PluginId; + return $"Plugin: {PluginId.ToString()}"; } } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index bef6c4b..a0a51fe 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -170,7 +170,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex .Where(x => x.IsClass && x.IsSealed) .Where(x => x.GetInterface(typeof(IPlugin).Name) != null)) { - var id = $"{type.Namespace?.ToLower()}"; + var id = new ComponentId(type.Namespace); var name = type.Assembly.GetCustomAttribute()?.Title; var icon = string.Empty; var description = type.Assembly.GetCustomAttribute()?.Description; @@ -238,7 +238,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex Host = _httpServerContext }; - hasUnfulfilledDependencies = HasUnfulfilledDependencies(id, dependencies); + hasUnfulfilledDependencies = HasUnfulfilledDependencies(id, dependencies.Select(x => new ComponentId(x))); if (hasUnfulfilledDependencies) { @@ -326,7 +326,7 @@ private void CheckUnfulfilledDependencies() var hasUnfulfilledDependencies = HasUnfulfilledDependencies ( unfulfilledDependencies.Key, - unfulfilledDependencies.Value.Dependencies + unfulfilledDependencies.Value.Dependencies.Select(x => new ComponentId(x)) ); if (!hasUnfulfilledDependencies) @@ -356,12 +356,12 @@ private void CheckUnfulfilledDependencies() /// The id of the plugin. /// The dependencies to check. /// True if dependencies exist, false otherwise - private bool HasUnfulfilledDependencies(string id, IEnumerable dependencies) + private bool HasUnfulfilledDependencies(IComponentId id, IEnumerable dependencies) { var hasUnfulfilledDependencies = false; foreach (var dependency in dependencies - .Where(x => !_dictionary.ContainsKey(x.ToLower()))) + .Where(x => !_dictionary.ContainsKey(x))) { // dependency was not fulfilled hasUnfulfilledDependencies = true; @@ -391,7 +391,7 @@ public IPluginContext GetPlugin(string pluginId) .Where ( x => x.PluginContext != null && - x.PluginContext.PluginId.Equals(pluginId, StringComparison.OrdinalIgnoreCase) + x.PluginContext.PluginId.ToString().Equals(pluginId) ) .Select(x => x.PluginContext) .FirstOrDefault(); @@ -458,7 +458,7 @@ public IEnumerable GetAssociatedApplications(IPluginContext /// The plugin item or null. private PluginItem GetPluginItem(IPluginContext pluginContext) { - var pluginId = pluginContext?.PluginId?.ToLower(); + var pluginId = pluginContext?.PluginId; if (pluginId == null || !_dictionary.TryGetValue(pluginId, out PluginItem value)) { @@ -602,7 +602,7 @@ private void Log() .Where ( x => x.Value.PluginClass.Assembly - .GetCustomAttribute(typeof(SystemPluginAttribute)) != null + .GetCustomAttribute() != null ) .Select(x => I18N.Translate ( diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index c7f44df..b2b1087 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; @@ -37,7 +38,7 @@ public class ResourceContext : IResourceContext /// /// Returns the resource id. /// - public string EndpointId { get; internal set; } + public IComponentId EndpointId { get; internal set; } /// /// Returns the parent or null if not used. diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 8a4b6cb..428e9dc 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -169,7 +169,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// Returns the endpoint id. /// - public string EndpointId { get; internal set; } + public IComponentId EndpointId { get; internal set; } /// /// Returns the parent or null if not used. diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 6e4dccf..82015d8 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -359,7 +359,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.SettingPageContext.EndpointId.Equals(pageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + var item = v.Value.Where(x => x.SettingPageContext.EndpointId.ToString().Equals(pageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); if (item != null) { return new SettingPageSearchResult() { Group = v.Key, Item = item }; diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 38d9f92..6d0d112 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; @@ -34,7 +35,7 @@ public class SettingPageContext : ISettingPageContext /// /// Returns the unique identifier for the setting page. /// - public string EndpointId { get; internal set; } + public IComponentId EndpointId { get; internal set; } /// /// Returns the setting page title. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 51a8c7b..c08e2eb 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -241,7 +241,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(IApplicationContext applicationContext) where T : I public UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint { var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T), endpointContext.ApplicationContext) - .Where(x => x.EndpointId.Equals(endpointContext.EndpointId, StringComparison.OrdinalIgnoreCase)); + .Where(x => x.EndpointId.Equals(endpointContext.EndpointId)); var node = _root.GetPreOrder() .Where(x => endpointContexts.Contains(x.EndpointContext)) @@ -462,7 +462,7 @@ private void Log() ( "webexpress:sitemapmanager.preorder", " " + x.ToString().PadRight(60), - x.EndpointContext?.EndpointId ?? "" + x.EndpointContext?.EndpointId.ToString() ?? "" )); foreach (var node in preorder) diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs index c6bc891..529c456 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs @@ -21,22 +21,22 @@ public interface IStatusPageContext : IContext IApplicationContext ApplicationContext { get; } /// - /// Returns or sets the status id. + /// Returns the status page id. /// - string StatusId { get; } + IComponentId StatusPageId { get; } /// - /// Returns or sets the status code. + /// Returns the status code. /// int StatusCode { get; } /// - /// Returns or sets the status title. + /// Returns the status title. /// string StatusTitle { get; } /// - /// Returns or sets the status icon. + /// Returns the status icon. /// UriResource StatusIcon { get; } } diff --git a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs index 4808081..22d9f86 100644 --- a/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs +++ b/src/WebExpress.WebCore/WebStatusPage/Model/StatusPageItem.cs @@ -9,11 +9,6 @@ namespace WebExpress.WebCore.WebStatusPage.Model /// internal class StatusPageItem { - /// - /// Returns the status page id. - /// - public string StatusPageId { get; internal set; } - /// /// Returns the associated plugin context. /// @@ -52,7 +47,7 @@ public void Dispose() /// The status page element in its string representation. public override string ToString() { - return $"StatusPage '{StatusPageId}'"; + return $"StatusPage '{StatusPageContext?.StatusPageId}'"; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs index 3473dde..40b34a5 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -20,9 +21,9 @@ public class StatusPageContext : IStatusPageContext public IApplicationContext ApplicationContext { get; internal set; } /// - /// Returns or sets the status id. + /// Returns the status id. /// - public string StatusId { get; internal set; } + public IComponentId StatusPageId { get; internal set; } /// /// Returns the status code. @@ -45,7 +46,7 @@ public class StatusPageContext : IStatusPageContext /// A string that represents the current object. public override string ToString() { - return $"StatusPage: {StatusId}"; + return $"StatusPage: {StatusPageId.ToString()}"; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 704c6f5..c7f8175 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -108,7 +108,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IStatusPage).Name) != null)) { - var id = resource.FullName?.ToLower(); + var id = new ComponentId(resource.FullName); var statusResponse = typeof(ResponseInternalServerError); var icon = string.Empty; var title = resource.Name; @@ -155,7 +155,7 @@ private void Register(IPluginContext pluginContext, IEnumerable().StatusCode; var statusPageContext = new StatusPageContext() { - StatusId = id, + StatusPageId = id, PluginContext = pluginContext, ApplicationContext = applicationContext, StatusCode = statusCode, @@ -165,7 +165,6 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Sat, 7 Dec 2024 11:36:04 +0100 Subject: [PATCH 050/162] add inheritance of scopes - consider inheritance of scopes --- .../Fixture/UnitTestFixture.cs | 14 +++++++------- .../Manager/UnitTestFragmentManager.cs | 17 ++++++++--------- .../WebComponent/ComponentId.cs | 4 ++-- .../WebFragment/FragmentManager.cs | 2 +- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index 72c6764..fd9f939 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -56,7 +56,7 @@ public static IHttpServerContext CreateHttpServerContextMock() /// The component hub. public static ComponentHub CreateComponentHubMock() { - var ctorComponentManager = typeof(ComponentHub).GetConstructor + var ctorComponentHub = typeof(ComponentHub).GetConstructor ( BindingFlags.NonPublic | BindingFlags.Instance, null, @@ -64,15 +64,15 @@ public static ComponentHub CreateComponentHubMock() null ); - var componentManager = (ComponentHub)ctorComponentManager.Invoke([CreateHttpServerContextMock()]); + var componentHub = (ComponentHub)ctorComponentHub.Invoke([CreateHttpServerContextMock()]); // set static field in the webex class var type = typeof(WebEx); var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); - field.SetValue(null, componentManager); + field.SetValue(null, componentHub); - return componentManager; + return componentHub; } /// @@ -81,12 +81,12 @@ public static ComponentHub CreateComponentHubMock() /// The component hub. public static ComponentHub CreateAndRegisterComponentHubMock() { - var componentManager = CreateComponentHubMock(); - var pluginManager = componentManager.PluginManager as PluginManager; + var componentHub = CreateComponentHubMock(); + var pluginManager = componentHub.PluginManager as PluginManager; pluginManager.Register(); - return componentManager; + return componentHub; } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 1c33b16..dabbd93 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -1,7 +1,7 @@ -using System.Text; -using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebScope; namespace WebExpress.WebCore.Test.Manager { @@ -83,22 +83,21 @@ public void Id(Type applicationType, Type fragmentType, string id) /// Test the process function of the fragment handler. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA), "TestFragmentA")] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB), "TestFragmentB")] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB), "TestFragmentA")] - public void Process(Type applicationType, Type sectionType, Type scopeType, string expected) + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA))] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB))] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB))] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(IScope))] + public void Process(Type applicationType, Type sectionType, Type scopeType) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); - var builder = new StringBuilder(); // test execution componentHub.FragmentManager.Process(renderContext, sectionType); - renderContext.VisualTree.Content.ToString(builder, 0); - Assert.Contains(expected, builder.ToString()); + Assert.NotEmpty(renderContext.VisualTree.Content.ToString()); } } } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentId.cs b/src/WebExpress.WebCore/WebComponent/ComponentId.cs index 57bd4fc..65fb013 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentId.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentId.cs @@ -27,11 +27,11 @@ public static implicit operator ComponentId(string id) } /// - /// Defines an explicit conversion of a to a string. + /// Defines an implicit conversion of a to a string. /// /// The to convert. /// The identifier of the component as a string. - public static explicit operator string(ComponentId componentId) + public static implicit operator string(ComponentId componentId) { return componentId._id; } diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index ba3d585..57e55e5 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -450,7 +450,7 @@ public void Process(IRenderContext renderContext, Type section) .SelectMany(x => x.Value) .Where(x => x.Key == section) .SelectMany(x => x.Value) - .Where(x => scopes.Any(y => x.Key == y)) + .Where(x => scopes.Any(y => y.IsAssignableFrom(x.Key))) .SelectMany(x => x.Value) .OrderBy(x => x.Order); From 3bddf94808ceca997da729b0a73b9a53675fd718 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 7 Dec 2024 16:34:09 +0100 Subject: [PATCH 051/162] refactoring - uses the rendering function in IFragment --- .../Manager/UnitTestFragmentManager.cs | 5 +++-- src/WebExpress.WebCore.Test/TestFragmentA.cs | 5 +++-- src/WebExpress.WebCore.Test/TestFragmentB.cs | 5 +++-- .../WebFragment/FragmentManager.cs | 16 +++++++++++----- .../WebFragment/IFragment.cs | 8 +++++--- .../WebFragment/IFragmentManager.cs | 6 ++++-- .../WebFragment/Model/FragmentItem.cs | 19 ++++++++++--------- 7 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index dabbd93..e923945 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -95,9 +95,10 @@ public void Process(Type applicationType, Type sectionType, Type scopeType) var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); // test execution - componentHub.FragmentManager.Process(renderContext, sectionType); + var html = componentHub.FragmentManager.Render(renderContext, sectionType); - Assert.NotEmpty(renderContext.VisualTree.Content.ToString()); + Assert.NotNull(html); + Assert.NotEmpty(html.ToString()); } } } diff --git a/src/WebExpress.WebCore.Test/TestFragmentA.cs b/src/WebExpress.WebCore.Test/TestFragmentA.cs index e6060b4..4fd1ca4 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentA.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentA.cs @@ -46,9 +46,10 @@ public TestFragmentA(IComponentHub componentHub, IFragmentContext fragmentContex /// Processes the fragments in the specified render context. /// /// The context in which rendering occurs. - public void Process(IRenderContext renderContext) + /// An HTML node representing the rendered fragments. + public IHtmlNode Render(IRenderContext renderContext) { - renderContext.VisualTree.Content = new HtmlText("TestFragmentA"); + return new HtmlText("TestFragmentA"); } /// diff --git a/src/WebExpress.WebCore.Test/TestFragmentB.cs b/src/WebExpress.WebCore.Test/TestFragmentB.cs index 8520660..97e1acb 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentB.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentB.cs @@ -38,9 +38,10 @@ public TestFragmentB(IComponentHub componentHub, IFragmentContext fragmentContex /// Processes the fragments in the specified render context. /// /// The context in which rendering occurs. - public void Process(IRenderContext renderContext) + /// An HTML node representing the rendered fragments. + public IHtmlNode Render(IRenderContext renderContext) { - renderContext.VisualTree.Content = new HtmlText("TestFragmentB"); + return new HtmlText("TestFragmentB"); } /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 57e55e5..b24f2aa 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -7,6 +7,7 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebFragment.Model; +using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; @@ -436,11 +437,12 @@ public IEnumerable GetFragments(IApplicationContext applicatio } /// - /// Processes the fragments for a given section within the specified render context. + /// Converts the fragments to HTML for a given section within the specified render context. /// /// The context in which rendering occurs. /// The section where the fragment is embedded. - public void Process(IRenderContext renderContext, Type section) + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + public IHtmlNode Render(IRenderContext renderContext, Type section) { var scopes = renderContext?.PageContext?.Scopes ?? []; @@ -454,10 +456,14 @@ public void Process(IRenderContext renderContext, Type section) .SelectMany(x => x.Value) .OrderBy(x => x.Order); - foreach (var item in items) + var nodes = items.Select(x => x.Render(renderContext)).ToList(); + + return nodes.Count switch { - item.Process(renderContext); - } + 0 => null, + 1 => nodes.First(), + _ => new HtmlElementTextContentDiv(nodes) + }; } /// diff --git a/src/WebExpress.WebCore/WebFragment/IFragment.cs b/src/WebExpress.WebCore/WebFragment/IFragment.cs index 26c6cde..28566f8 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragment.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragment.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebPage; namespace WebExpress.WebCore.WebFragment @@ -9,9 +10,10 @@ namespace WebExpress.WebCore.WebFragment public interface IFragment : IComponent { /// - /// Processes the fragments in the specified render context. + /// Convert the fragment to HTML. /// - /// The context in which rendering occurs. - void Process(IRenderContext renderContext); + /// The context in which the fragment is rendered. + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + IHtmlNode Render(IRenderContext renderContext); } } diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index e7bf536..a3bbba2 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSection; @@ -86,10 +87,11 @@ public interface IFragmentManager : IComponentManager IEnumerable GetFragments(IApplicationContext applicationContext, Type section, IEnumerable scopes); /// - /// Processes the fragments for a given section within the specified render context. + /// Converts the fragments to HTML for a given section within the specified render context. /// /// The context in which rendering occurs. /// The section where the fragment is embedded. - void Process(IRenderContext renderContext, Type section); + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + IHtmlNode Render(IRenderContext renderContext, Type section); } } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index 42e1b73..a43d92b 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -4,6 +4,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebHtml; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -77,23 +78,23 @@ public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerCon /// Processes the fragments for a given section within the specified render context. /// /// The context in which rendering occurs. - public void Process(IRenderContext renderContext) + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + public IHtmlNode Render(IRenderContext renderContext) { var instance = _instance; - if (instance == null) + instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + + if (Cache) { - instance = ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + _instance = instance; } if (CheckControl(renderContext)) { - instance.Process(renderContext); + return instance.Render(renderContext); } - if (Cache) - { - _instance = instance; - } + return null; } /// @@ -103,7 +104,7 @@ public void Process(IRenderContext renderContext) /// True if the fragment is active, false otherwise. private bool CheckControl(IRenderContext renderContext) { - return !FragmentContext.Conditions.Any() || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); + return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); } /// From e3c0cf7a013d3821b197e8216a77fbeca4610db0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 7 Dec 2024 16:52:36 +0100 Subject: [PATCH 052/162] bug fixes --- .../Manager/UnitTestPageManager.cs | 8 ++++---- .../Manager/UnitTestResourceManager.cs | 4 ++-- .../Manager/UnitTestRestApiManager.cs | 4 ++-- .../Manager/UnitTestSettingPageManager.cs | 8 ++++---- .../Manager/UnitTestStatusPageManager.cs | 4 ++-- src/WebExpress.WebCore/WebFragment/FragmentManager.cs | 6 ++++++ 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index dc38a99..0e654a3 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -78,7 +78,7 @@ public void Id(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationC), typeof(TestPageB), "webindex:pageb.label")] [InlineData(typeof(TestApplicationC), typeof(TestPageC), "webindex:pagec.label")] - public void Title(Type applicationType, Type resourceType, string id) + public void Title(Type applicationType, Type resourceType, string title) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -86,7 +86,7 @@ public void Title(Type applicationType, Type resourceType, string id) var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, page.PageTitle); + Assert.Equal(title, page.PageTitle); } /// @@ -102,7 +102,7 @@ public void Title(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/server")] [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/server/resa")] [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/server")] - public void ContextPath(Type applicationType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -110,7 +110,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, page.ContextPath); + Assert.Equal(path, page.ContextPath); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index b8b97fe..5a05211 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -84,7 +84,7 @@ public void Id(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/server")] [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/server")] - public void ContextPath(Type applicationType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -92,7 +92,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, resource.ContextPath); + Assert.Equal(path, resource.ContextPath); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index b34fa24..03bd2f9 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -77,7 +77,7 @@ public void Id(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/server/1")] [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/server/1/apia/2")] [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/server/1/apia/2/apib/3")] - public void ContextPath(Type applicationType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -85,7 +85,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, api?.ContextPath); + Assert.Equal(path, api?.ContextPath); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 43ac5c4..6cf868e 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -72,7 +72,7 @@ public void Id(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webindex:settingpagea.label")] [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webindex:settingpageb.label")] - public void Title(Type applicationType, Type resourceType, string id) + public void Title(Type applicationType, Type resourceType, string title) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -80,7 +80,7 @@ public void Title(Type applicationType, Type resourceType, string id) var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, settingPage.SettingPageTitle); + Assert.Equal(title, settingPage.SettingPageTitle); } /// @@ -93,7 +93,7 @@ public void Title(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/server/appb")] [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/server")] [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/server")] - public void ContextPath(Type applicationType, Type resourceType, string id) + public void ContextPath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -101,7 +101,7 @@ public void ContextPath(Type applicationType, Type resourceType, string id) var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(id, settingPage.ContextPath); + Assert.Equal(path, settingPage.ContextPath); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 5b4c089..9ec5874 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -76,7 +76,7 @@ public void Id(Type applicationType, Type statusPageType, string id) [InlineData(typeof(TestApplicationC), typeof(TestStatusPage400), "webindex:homepage.label")] [InlineData(typeof(TestApplicationC), typeof(TestStatusPage404), "webindex:homepage.label")] [InlineData(typeof(TestApplicationC), typeof(TestStatusPage500), "webindex:homepage.label")] - public void Title(Type applicationType, Type resourceType, string id) + public void Title(Type applicationType, Type resourceType, string title) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -84,7 +84,7 @@ public void Title(Type applicationType, Type resourceType, string id) var statusPage = componentHub.StatusPageManager.GetStatusPage(application, resourceType); // test execution - Assert.Equal(id, statusPage.StatusTitle); + Assert.Equal(title, statusPage.StatusTitle); } /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index b24f2aa..aafae84 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -177,6 +177,12 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Sat, 7 Dec 2024 18:44:43 +0100 Subject: [PATCH 053/162] bug fixes --- .../Manager/UnitTestInternationalization.cs | 1 + src/WebExpress.WebCore/HttpServer.cs | 20 +++++------ .../InternationalizationManager.cs | 4 +-- .../Internationalization/de | 3 +- .../Internationalization/en | 3 +- .../WebApplication/ApplicationManager.cs | 16 ++++----- src/WebExpress.WebCore/WebAsset/Asset.cs | 2 +- .../WebAsset/AssetManager.cs | 6 ++-- .../WebComponent/ComponentHub.cs | 20 +++++------ .../WebEndpoint/EndpointManager.cs | 2 +- .../WebEvent/EventManager.cs | 10 +++--- src/WebExpress.WebCore/WebEx.cs | 30 ++++++++-------- .../WebFragment/FragmentManager.cs | 31 +++++++---------- .../WebIdentity/IdentityManager.cs | 10 +++--- src/WebExpress.WebCore/WebJob/Cron.cs | 8 ++--- src/WebExpress.WebCore/WebJob/JobManager.cs | 14 ++++---- src/WebExpress.WebCore/WebLog/LogManager.cs | 2 +- .../WebPackage/PackageManager.cs | 14 ++++---- src/WebExpress.WebCore/WebPage/PageManager.cs | 6 ++-- .../WebPlugin/PluginManager.cs | 34 +++++++++---------- .../WebResource/ResourceAsset.cs | 2 +- .../WebResource/ResourceFile.cs | 2 +- .../WebResource/ResourceManager.cs | 6 ++-- .../WebRestAPI/RestApiManager.cs | 8 ++--- .../WebSession/SessionManager.cs | 2 +- .../WebSettingPage/SettingPageManager.cs | 2 +- .../WebSitemap/SitemapManager.cs | 8 ++--- .../WebStatusPage/StatusPageManager.cs | 10 +++--- src/WebExpress.WebCore/WebTask/TaskManager.cs | 2 +- 29 files changed, 138 insertions(+), 140 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs index 1d64c9a..9b3247c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestInternationalization.cs @@ -68,6 +68,7 @@ public void GetDefaultCulture() [InlineData("webexpress.webcore.test:unit.test.message", "Dies ist ein Test", "de", "webexpress.webcore.test")] [InlineData("webexpress.webcore.test:welcome.message", "Welcome 'Max' to our application!", "en", null, "Max")] [InlineData("welcome.message", "Welcome 'Max' to our application!", "en", "webexpress.webcore.test", "Max")] + [InlineData("webexpress.webcore:app.startup", "Startup", "en", null)] [InlineData("non.existent.key", "non.existent.key", "de")] public void Translate(string key, string excepted, string cultureName = null, string pluginID = null, params object[] param) { diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 7ca931f..fe67b56 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -101,12 +101,12 @@ public void Start() { if (HttpServerContext != null && HttpServerContext.Log != null) { - HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.run")); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:httpserver.run")); } if (!HttpListener.IsSupported) { - HttpServerContext.Log.Error(message: I18N.Translate("webexpress:httpserver.notsupported")); + HttpServerContext.Log.Error(message: I18N.Translate("webexpress.webcore:httpserver.notsupported")); } var logger = new LogFactory(); @@ -146,7 +146,7 @@ public void Start() Kestrel.StartAsync(this, ServerToken); - HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.start"), args: [ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString()]); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:httpserver.start"), args: [ExecutionTime.ToShortDateString(), ExecutionTime.ToLongTimeString()]); Started?.Invoke(this, new EventArgs()); } @@ -169,7 +169,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, End .Union(asterisk ? Dns.GetHostEntry("localhost").AddressList : []) .Where(x => x.AddressFamily == AddressFamily.InterNetwork || x.AddressFamily == AddressFamily.InterNetworkV6); - HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.endpoint"), args: endPoint.Uri); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:httpserver.endpoint"), args: endPoint.Uri); foreach (var ipAddress in addressList) { @@ -185,7 +185,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, End } catch (Exception ex) { - HttpServerContext.Log.Error(message: I18N.Translate("webexpress:httpserver.listen.exeption"), args: endPoint); + HttpServerContext.Log.Error(message: I18N.Translate("webexpress.webcore:httpserver.listen.exeption"), args: endPoint); HttpServerContext.Log.Exception(ex); } @@ -200,7 +200,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, IPE { serverOptions.Value.Listen(endPoint); - HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.listen"), args: endPoint.ToString()); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:httpserver.listen"), args: endPoint.ToString()); } /// @@ -219,7 +219,7 @@ private void AddEndpoint(OptionsWrapper serverOptions, IPE configure.UseHttps(cert); }); - HttpServerContext.Log.Info(message: I18N.Translate("webexpress:httpserver.listen"), args: endPoint.ToString()); + HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:httpserver.listen"), args: endPoint.ToString()); } /// @@ -245,10 +245,10 @@ private Response HandleClient(HttpContext context) var culture = request.Culture; var uri = request?.Uri; - HttpServerContext.Log.Debug(message: I18N.Translate("webexpress:httpserver.connected"), args: context.RemoteEndPoint); + HttpServerContext.Log.Debug(message: I18N.Translate("webexpress.webcore:httpserver.connected"), args: context.RemoteEndPoint); HttpServerContext.Log.Info(I18N.Translate ( - "webexpress:httpserver.request", + "webexpress.webcore:httpserver.request", context.RemoteEndPoint, ++RequestNumber, $"{request?.Method} {request?.Uri} {request?.Protocoll}" @@ -353,7 +353,7 @@ private Response HandleClient(HttpContext context) HttpServerContext.Log.Info(I18N.Translate ( - "webexpress:httpserver.request.done", + "webexpress.webcore:httpserver.request.done", context?.RemoteEndPoint, RequestNumber, stopwatch.ElapsedMilliseconds, diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index 56d5eb4..b65ee06 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -56,7 +56,7 @@ private InternationalizationManager(IComponentHub componentHub, IHttpServerConte HttpServerContext.Log.Debug ( - Translate("webexpress:internationalizationmanager.initialization") + Translate("webexpress.webcore:internationalizationmanager.initialization") ); } @@ -71,7 +71,7 @@ public void Register(IPluginContext pluginContext) HttpServerContext.Log.Debug ( - Translate("webexpress:internationalizationmanager.register", pluginId) + Translate("webexpress.webcore:internationalizationmanager.register", pluginId) ); } diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 3b1ed1a..a21456b 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -73,6 +73,7 @@ pluginmanager.fulfilleddependencies=Das Plugin '{0}' erfüllt alle Abhängigkeit pluginmanager.unfulfilleddependencies=Das Plugin '{0}' erfüllt eine Abhängigkeit zu dem Plugin '{1}' nicht. pluginmanager.applicationless=Das Plugin '{0}' besitzt keine Angaben zur Anwendung. +applicationmanager.titel=Anwendungsmanager: applicationmanager.initialization=Der Anwendungsmanager wurde initialisiert. applicationmanager.register=Die Anwendung '{0}' wurde erstellt und im Anwendungsmanager registriert. applicationmanager.duplicate=Die Anwendung '{0}' wurde bereits im Anwendungsmanager registriert. @@ -108,13 +109,13 @@ statuspagemanager.duplicat=Der Status '{0}' wurde bereits registriert. Die Statu statuspagemanager.statuscodeless=Ein Statuscode wurde der Ressource '{1}' für die Anwendung '{0}' nicht zugewiesen. statuspagemanager.statuspage=Statuscode: '{0}' +sitemapmanager..titel=Sitemap: sitemapmanager.initialization=Der Sitemap-Manager wurde initialisiert. sitemapmanager.refresh=Die Sitemap wird neu aufgebaut. sitemapmanager.alreadyassigned=Der Knoten der Sitemap '{0}' ist bereits zugewiesen. Die Ressource '{1}' wird nicht in die Sitemap aufgenommen. sitemapmanager.addresource=Die Ressource '{0}' wurde in der Sitemap registriert. sitemapmanager.addresource.error=Die Ressource '{0}' konnte nicht in der Sitemap hinzugefügt werden. sitemapmanager.preorder={0} => {1} -sitemapmanager.sitemap=Sitemap: sitemapmanager.merge.error=Die beiden Sitemaps '{0}' und '{1}' konnten nicht gemerdged werden. sessionmanager.initialization=Der Sessionmanager wurde initialisiert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index d336605..97d43df 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -73,6 +73,7 @@ pluginmanager.fulfilleddependencies=The plugin '{0}' fulfills all dependencies. pluginmanager.unfulfilleddependencies=The plugin '{0}' does not fulfill a dependency on the plugin '{1}'. pluginmanager.applicationless=The plugin '{0}' does not have any information about an application. +applicationmanager.titel=Application manager: applicationmanager.initialization=The application manager has been initialized. applicationmanager.registerapplication=The application '{0}' has been created and registered in the application manager. applicationmanager.duplicateapplication=The application '{0}' has already been registered in the application manager. @@ -108,13 +109,13 @@ statuspagemanager.duplicat=The status '{0}' has already been registered. Therefo statuspagemanager.statuscodeless=A status code has not been assigned to the resource '{1}' for the application '{0}'. statuspagemanager.resource=Status code: '{0}' +sitemapmanager.titel=Sitemap: sitemapmanager.initialization=The sitemap manager has been initialized. sitemapmanager.refresh=The sitemap will be rebuilt. sitemapmanager.alreadyassigned=The node of the sitemap '{0}' is already assigned. The resource '{1}' is not included in the sitemap. sitemapmanager.addresource=The resource '{0}' has been registered in the sitemap. sitemapmanager.addresource.error=Could not add resource '{0}' in the sitemap. sitemapmanager.preorder={0} => {1} -sitemapmanager.sitemap=Sitemap: sitemapmanager.merge.error=The two sitemaps '{0}' and '{1}' could not be merged. sessionmanager.initialization=The session manager has been initialized. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 705a6a2..6c73a0b 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -54,7 +54,7 @@ private ApplicationManager(IComponentHub componentHub, IHttpServerContext httpSe _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:applicationmanager.initialization") + I18N.Translate("webexpress.webcore:applicationmanager.initialization") ); } @@ -154,7 +154,7 @@ private void Register(IPluginContext pluginContext) _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:applicationmanager.register", id) + I18N.Translate("webexpress.webcore:applicationmanager.register", id) ); // raises the AddApplication event @@ -164,7 +164,7 @@ private void Register(IPluginContext pluginContext) { _httpServerContext.Log.Warning ( - I18N.Translate("webexpress:applicationmanager.duplicate", id) + I18N.Translate("webexpress.webcore:applicationmanager.duplicate", id) ); } } @@ -315,7 +315,7 @@ public void Boot(IPluginContext pluginContext) ( I18N.Translate ( - "webexpress:applicationmanager.application.processing.start", + "webexpress.webcore:applicationmanager.application.processing.start", applicationItem.ApplicationContext.ApplicationId) ); @@ -325,7 +325,7 @@ public void Boot(IPluginContext pluginContext) ( I18N.Translate ( - "webexpress:applicationmanager.application.processing.end", + "webexpress.webcore:applicationmanager.application.processing.end", applicationItem.ApplicationContext.ApplicationId ) ); @@ -340,7 +340,7 @@ public void Boot(IPluginContext pluginContext) ( I18N.Translate ( - "webexpress:applicationmanager.application.boot.notfound", + "webexpress.webcore:applicationmanager.application.boot.notfound", pluginContext.PluginId ) ); @@ -405,14 +405,14 @@ private void Log() using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List { - I18N.Translate("webexpress:applicationmanager") + I18N.Translate("webexpress.webcore:applicationmanager.titel") }; foreach (var applicationContext in Applications) { list.Add ( - I18N.Translate("webexpress:applicationmanager.application", applicationContext.ApplicationId) + I18N.Translate("webexpress.webcore:applicationmanager.application", applicationContext.ApplicationId) ); } diff --git a/src/WebExpress.WebCore/WebAsset/Asset.cs b/src/WebExpress.WebCore/WebAsset/Asset.cs index f119f6e..5423486 100644 --- a/src/WebExpress.WebCore/WebAsset/Asset.cs +++ b/src/WebExpress.WebCore/WebAsset/Asset.cs @@ -125,7 +125,7 @@ public Response Process(Request request) _httpServerContext.Log.Debug(I18N.Translate ( - "webexpress:asset.file", + "webexpress.webcore:asset.file", request.RemoteEndPoint, request.Uri )); diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index c236483..60973d3 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -91,7 +91,7 @@ private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerCo _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:assetmanager.initialization") + I18N.Translate("webexpress.webcore:assetmanager.initialization") ); } @@ -166,7 +166,7 @@ private void Register(IPluginContext pluginContext, IEnumerable @@ -266,7 +266,7 @@ private IComponentManager CreateInstance(Type componentType) ( _internationalizationManager.Translate ( - "webexpress:componentmanager.wrongtype", + "webexpress.webcore:componentmanager.wrongtype", componentType?.FullName, typeof(IComponentManager).FullName ) ); @@ -349,7 +349,7 @@ internal void Register(IPluginContext pluginContext) _httpServerContext.Log.Debug ( - _internationalizationManager.Translate("webexpress:componentmanager.register", id) + _internationalizationManager.Translate("webexpress.webcore:componentmanager.register", id) ); // raises the AddComponent event @@ -359,7 +359,7 @@ internal void Register(IPluginContext pluginContext) { _httpServerContext.Log.Warning ( - _internationalizationManager.Translate("webexpress:componentmanager.duplicate", id) + _internationalizationManager.Translate("webexpress.webcore:componentmanager.duplicate", id) ); } } @@ -413,7 +413,7 @@ internal void Execute() { _httpServerContext.Log.Debug ( - _internationalizationManager.Translate("webexpress:componentmanager.execute") + _internationalizationManager.Translate("webexpress.webcore:componentmanager.execute") ); _packageManager.Execute(); @@ -427,7 +427,7 @@ internal void ShutDown() { _httpServerContext.Log.Debug ( - _internationalizationManager.Translate("webexpress:componentmanager.shutdown") + _internationalizationManager.Translate("webexpress.webcore:componentmanager.shutdown") ); } @@ -472,7 +472,7 @@ public void Remove(IPluginContext pluginContext) _httpServerContext.Log.Debug ( - _internationalizationManager.Translate("webexpress:componentmanager.remove") + _internationalizationManager.Translate("webexpress.webcore:componentmanager.remove") ); } } @@ -506,7 +506,7 @@ private void Log() using var frame = new LogFrameSimple(_httpServerContext.Log); var output = new List { - _internationalizationManager.Translate("webexpress:componentmanager.component") + _internationalizationManager.Translate("webexpress.webcore:componentmanager.component") }; foreach (var pluginContext in PluginManager.Plugins) @@ -514,7 +514,7 @@ private void Log() output.Add ( string.Empty.PadRight(2) + - _internationalizationManager.Translate("webexpress:pluginmanager.plugin", pluginContext.PluginId) + _internationalizationManager.Translate("webexpress.webcore:pluginmanager.plugin", pluginContext.PluginId) ); } diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 799cd54..0623992 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -45,7 +45,7 @@ private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServe _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:endpointmanager.initialization") + I18N.Translate("webexpress.webcore:endpointmanager.initialization") ); } diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index 83b7cc8..d421c0b 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -58,7 +58,7 @@ internal EventManager(IComponentHub componentHub, IHttpServerContext httpServerC ( I18N.Translate ( - "webexpress:eventmanager.initialization" + "webexpress.webcore:eventmanager.initialization" ) ); } @@ -173,7 +173,7 @@ private void Register(IPluginContext pluginContext, IEnumerable output, in // string.Empty.PadRight(deep) + // I18N.Translate // ( - // "webexpress:eventmanager.job", + // "webexpress.webcore:eventmanager.job", // scheduleItem.JobId, // scheduleItem.ModuleContext // ) diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 9a217f5..20e210b 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -222,21 +222,21 @@ private void Initialization(string args, string configFile) // log program start _httpServer.HttpServerContext.Log.Seperator('/'); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.startup")); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.startup")); _httpServer.HttpServerContext.Log.Info(message: "".PadRight(80, '-')); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.version"), args: Version); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.arguments"), args: args); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.workingdirectory"), args: Environment.CurrentDirectory); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.packagebase"), args: config.PackageBase); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.assetbase"), args: config.AssetBase); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.database"), args: config.DataBase); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.configurationdirectory"), args: Path.GetDirectoryName(configFile)); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.configuration"), args: Path.GetFileName(configFile)); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.logdirectory"), args: Path.GetDirectoryName(_httpServer.HttpServerContext.Log.Filename)); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.log"), args: Path.GetFileName(_httpServer.HttpServerContext.Log.Filename)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.version"), args: Version); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.arguments"), args: args); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.workingdirectory"), args: Environment.CurrentDirectory); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.packagebase"), args: config.PackageBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.assetbase"), args: config.AssetBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.database"), args: config.DataBase); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.configurationdirectory"), args: Path.GetDirectoryName(configFile)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.configuration"), args: Path.GetFileName(configFile)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.logdirectory"), args: Path.GetDirectoryName(_httpServer.HttpServerContext.Log.Filename)); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.log"), args: Path.GetFileName(_httpServer.HttpServerContext.Log.Filename)); foreach (var v in config.Endpoints) { - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.uri"), args: v.Uri); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.uri"), args: v.Uri); } _httpServer.HttpServerContext.Log.Seperator('='); @@ -278,9 +278,9 @@ private void Exit() // end of program log _httpServer.HttpServerContext.Log.Seperator('='); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.errors"), args: _httpServer.HttpServerContext.Log.ErrorCount); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.warnings"), args: _httpServer.HttpServerContext.Log.WarningCount); - _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress:app.done")); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.errors"), args: _httpServer.HttpServerContext.Log.ErrorCount); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.warnings"), args: _httpServer.HttpServerContext.Log.WarningCount); + _httpServer.HttpServerContext.Log.Info(message: I18N.Translate("webexpress.webcore:app.done")); _httpServer.HttpServerContext.Log.Seperator('/'); // Stop running diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index aafae84..5ee6bf9 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebFragment.Model; using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; @@ -477,24 +478,18 @@ public IHtmlNode Render(IRenderContext renderContext, Type section) /// private void Log() { - //output.Add - //( - // string.Empty.PadRight(deep) + - // I18N.Translate("webexpress.webui:fragmentmanager.titel") - //); - - //foreach (var fragmentItem in GetFragmentItems(pluginContext)) - //{ - // output.Add - // ( - // string.Empty.PadRight(deep + 2) + - // I18N.Translate - // ( - // "webexpress.webui:fragmentmanager.fragment", - // fragmentItem.FragmentClass.Name - // ) - // ); - //} + + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { + I18N.Translate("webexpress.webcore:fragmentmanager.titel") + }; + foreach (var fragment in Fragments) + { + list.Add(I18N.Translate("webexpress.webcore:fragmentmanager.fragment", fragment.FragmentId.ToString())); + } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs index ecb8203..d981692 100644 --- a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs +++ b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs @@ -72,7 +72,7 @@ private IdentityManager(IComponentHub componentHub, IHttpServerContext httpServe ( I18N.Translate ( - "webexpress:identitymanager.initialization" + "webexpress.webcore:identitymanager.initialization" ) ); } @@ -166,7 +166,7 @@ private void Register(IPluginContext pluginContext, IEnumerable Parse(string value, int minValue, int maxValue) } else { - Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.range"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress.webcore:schedulermanager.cron.range"), args: value); } } else if (range.Length == 1) @@ -122,17 +122,17 @@ private static IEnumerable Parse(string value, int minValue, int maxValue) } else { - Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.range"), args: result); + Context.Log.Warning(message: I18N.Translate("webexpress.webcore:schedulermanager.cron.range"), args: result); } } else { - Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.parseerror"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress.webcore:schedulermanager.cron.parseerror"), args: value); } } else { - Context.Log.Warning(message: I18N.Translate("webexpress:schedulermanager.cron.parseerror"), args: value); + Context.Log.Warning(message: I18N.Translate("webexpress.webcore:schedulermanager.cron.parseerror"), args: value); } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index cb4095d..5df6ee5 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -63,7 +63,7 @@ private JobManager(IComponentHub componentHub, IHttpServerContext httpServerCont ( I18N.Translate ( - "webexpress:jobmanager.initialization" + "webexpress.webcore:jobmanager.initialization" ) ); } @@ -184,7 +184,7 @@ private void Register(IPluginContext pluginContext, IEnumerable output, in string.Empty.PadRight(deep) + I18N.Translate ( - "webexpress:jobmanager.job", + "webexpress.webcore:jobmanager.job", scheduleItem.JobId, scheduleItem.ApplicationContext ) diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 1c2bdd5..58b9a70 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -44,7 +44,7 @@ private LogManager(IComponentHub componentHub, IHttpServerContext httpServerCont _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:logmanager.initialization") + I18N.Translate("webexpress.webcore:logmanager.initialization") ); } diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index 6e45341..9077e5a 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -61,7 +61,7 @@ private PackageManager(IComponentHub componentHub, IPluginManager pluginManager, _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:packagemanager.initialization") + I18N.Translate("webexpress.webcore:packagemanager.initialization") ); } @@ -86,7 +86,7 @@ internal void Execute() _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:packagemanager.existing", package.File) + I18N.Translate("webexpress.webcore:packagemanager.existing", package.File) ); if (package.State != PackageCatalogeItemState.Disable) @@ -133,7 +133,7 @@ public void Scan() ( I18N.Translate ( - "webexpress:packagemanager.scan", + "webexpress.webcore:packagemanager.scan", _httpServerContext.PackagePath ) ); @@ -164,7 +164,7 @@ public void Scan() ( I18N.Translate ( - "webexpress:packagemanager.add", + "webexpress.webcore:packagemanager.add", package ) ); @@ -180,7 +180,7 @@ public void Scan() ( I18N.Translate ( - "webexpress:packagemanager.remove", + "webexpress.webcore:packagemanager.remove", package ) ); @@ -296,7 +296,7 @@ private PackageCatalogItem LoadPackage(string file) ( I18N.Translate ( - "webexpress:packagemanager.packagenotfound", + "webexpress.webcore:packagemanager.packagenotfound", file ) ); @@ -338,7 +338,7 @@ private void SaveCatalog() _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:packagemanager.save") + I18N.Translate("webexpress.webcore:packagemanager.save") ); } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index b6b067d..68c82a5 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -97,7 +97,7 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:pagemanager.initialization") + I18N.Translate("webexpress.webcore:pagemanager.initialization") ); } @@ -374,7 +374,7 @@ private void Register(IPluginContext pluginContext, IEnumerable output, in string.Empty.PadRight(deep) + I18N.Translate ( - "webexpress:pagemanager.resource", + "webexpress.webcore:pagemanager.resource", resourcenItem?.PageContext?.EndpointId, string.Join(",", resourcenItem?.PageContext?.ApplicationContext?.ApplicationId) ) diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index a0a51fe..f4d5df5 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -52,7 +52,7 @@ private PluginManager(IComponentHub componentHub, IHttpServerContext httpServerC _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:pluginmanager.initialization") + I18N.Translate("webexpress.webcore:pluginmanager.initialization") ); } @@ -78,7 +78,7 @@ internal void Register() ( I18N.Translate ( - "webexpress:pluginmanager.load", + "webexpress.webcore:pluginmanager.load", assembly.GetName().Name, assembly.GetName().Version.ToString() ) @@ -129,7 +129,7 @@ internal IEnumerable Register(string pluginFile) ( I18N.Translate ( - "webexpress:pluginmanager.load", + "webexpress.webcore:pluginmanager.load", assembly.GetName().Name, assembly.GetName().Version.ToString() ) @@ -208,7 +208,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex // to many plugins, only one per assembly _httpServerContext.Log.Warning ( - I18N.Translate("webexpress:pluginmanager.tomany", type.FullName) + I18N.Translate("webexpress.webcore:pluginmanager.tomany", type.FullName) ); break; @@ -219,7 +219,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex // no application specified _httpServerContext.Log.Warning ( - I18N.Translate("webexpress:pluginmanager.applicationless", id) + I18N.Translate("webexpress.webcore:pluginmanager.applicationless", id) ); break; @@ -265,7 +265,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:pluginmanager.created", id) + I18N.Translate("webexpress.webcore:pluginmanager.created", id) ); OnAddPlugin(pluginContext); @@ -276,7 +276,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex { _httpServerContext.Log.Warning ( - I18N.Translate("webexpress:pluginmanager.duplicate", id) + I18N.Translate("webexpress.webcore:pluginmanager.duplicate", id) ); } @@ -341,7 +341,7 @@ private void CheckUnfulfilledDependencies() ( I18N.Translate ( - "webexpress:pluginmanager.fulfilleddependencies", + "webexpress.webcore:pluginmanager.fulfilleddependencies", unfulfilledDependencies.Key ) ); @@ -370,7 +370,7 @@ private bool HasUnfulfilledDependencies(IComponentId id, IEnumerable I18N.Translate ( - "webexpress:pluginmanager.pluginmanager.system", + "webexpress.webcore:pluginmanager.pluginmanager.system", x.Key )) ); @@ -619,7 +619,7 @@ private void Log() ) .Select(x => I18N.Translate ( - "webexpress:pluginmanager.pluginmanager.custom", + "webexpress.webcore:pluginmanager.pluginmanager.custom", x.Key )) ); @@ -627,7 +627,7 @@ private void Log() list.AddRange(_unfulfilledDependencies .Select(x => I18N.Translate ( - "webexpress:pluginmanager.pluginmanager.unfulfilleddependencies", + "webexpress.webcore:pluginmanager.pluginmanager.unfulfilleddependencies", x.Key )) ); diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index afe1a04..4a938cd 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -120,7 +120,7 @@ public override Response Process(Request request) request.HttpServerContext.Log.Debug(I18N.Translate ( - "webexpress:resource.file", + "webexpress.webcore:resource.file", request.RemoteEndPoint, request.Uri )); diff --git a/src/WebExpress.WebCore/WebResource/ResourceFile.cs b/src/WebExpress.WebCore/WebResource/ResourceFile.cs index b24ce89..32de325 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceFile.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceFile.cs @@ -110,7 +110,7 @@ public override Response Process(Request request) break; } - request.HttpServerContext.Log.Debug(I18N.Translate("webexpress:resource.file", request.RemoteEndPoint, request.Uri)); + request.HttpServerContext.Log.Debug(I18N.Translate("webexpress.webcore:resource.file", request.RemoteEndPoint, request.Uri)); return response; } diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 428e9dc..1f683fa 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -77,7 +77,7 @@ private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServe _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:resourcemanager.initialization") + I18N.Translate("webexpress.webcore:resourcemanager.initialization") ); } @@ -190,7 +190,7 @@ private void Register(IPluginContext pluginContext, IEnumerable output, in string.Empty.PadRight(deep) + I18N.Translate ( - "webexpress:resourcemanager.resource", + "webexpress.webcore:resourcemanager.resource", resourcenItem?.ResourceContext?.EndpointId, string.Join(",", resourcenItem.ResourceContext?.ApplicationContext?.ApplicationId) ) diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 82015d8..13fecfc 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -102,7 +102,7 @@ private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServer return new ResponseBadRequest() { - Content = I18N.Translate("webexpress:restapimanager.methodnotsupported", request.Method.ToString()) + Content = I18N.Translate("webexpress.webcore:restapimanager.methodnotsupported", request.Method.ToString()) }; } }; @@ -116,7 +116,7 @@ private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServer _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:restapimanager.initialization") + I18N.Translate("webexpress.webcore:restapimanager.initialization") ); } @@ -391,7 +391,7 @@ private void Register(IPluginContext pluginContext, IEnumerable output, in string.Empty.PadRight(deep) + I18N.Translate ( - "webexpress:restapimanager.resource", + "webexpress.webcore:restapimanager.resource", resourcenItem?.RestApiContext?.EndpointId, string.Join(",", resourcenItem?.RestApiContext?.ApplicationContext?.ApplicationId) ) diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index c862719..ffe6d43 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -27,7 +27,7 @@ internal SessionManager(IHttpServerContext context) _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:sessionmanager.initialization") + I18N.Translate("webexpress.webcore:sessionmanager.initialization") ); } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index c08e2eb..02dbd9c 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -274,7 +274,7 @@ private void Register(IPluginContext pluginContext, IEnumerable I18N.Translate ( - "webexpress:sitemapmanager.preorder", + "webexpress.webcore:sitemapmanager.preorder", " " + x.ToString().PadRight(60), x.EndpointContext?.EndpointId.ToString() ?? "" )); diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index c7f8175..86b662a 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -60,7 +60,7 @@ private StatusPageManager(IComponentHub componentHub, IHttpServerContext httpSer _httpServerContext.Log.Debug ( - I18N.Translate("webexpress:statuspagemanager.initialization") + I18N.Translate("webexpress.webcore:statuspagemanager.initialization") ); } @@ -144,7 +144,7 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Sat, 7 Dec 2024 22:59:18 +0100 Subject: [PATCH 054/162] refactoring --- .../Fixture/UnitTestFixture.cs | 4 +- .../InternationalizationManager.cs | 2 +- .../WebComponent/ComponentActivator.cs | 36 ++++++++++++++ .../WebComponent/ComponentHub.cs | 6 +-- .../WebComponent/IComponentHub.cs | 10 ++++ src/WebExpress.WebCore/WebEx.cs | 49 +++++++++---------- 6 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index fd9f939..1362b35 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -58,7 +58,7 @@ public static ComponentHub CreateComponentHubMock() { var ctorComponentHub = typeof(ComponentHub).GetConstructor ( - BindingFlags.NonPublic | BindingFlags.Instance, + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(HttpServerContext)], null @@ -67,7 +67,7 @@ public static ComponentHub CreateComponentHubMock() var componentHub = (ComponentHub)ctorComponentHub.Invoke([CreateHttpServerContextMock()]); // set static field in the webex class - var type = typeof(WebEx); + var type = typeof(WebEx); var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, componentHub); diff --git a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs index b65ee06..bc9ee28 100644 --- a/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs +++ b/src/WebExpress.WebCore/Internationalization/InternationalizationManager.cs @@ -92,7 +92,7 @@ public void Register(IEnumerable pluginContexts) /// /// The assembly that contains the key-value pairs to insert. /// The id of the plugin to which the internationalization data will be assigned. - internal void Register(Assembly assembly, string pluginId) + public void Register(Assembly assembly, string pluginId) { var assemblyName = assembly.GetName().Name.ToLower(); var name = assemblyName + ".internationalization."; diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index cead698..c976dab 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -55,6 +55,42 @@ public static T CreateInstance(Type responseType, IHttpServerContext httpServ return Activator.CreateInstance(responseType) as T; } + /// + /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. + /// + /// The type of the component manager, which must implement . + /// The reference to the context of the host. + /// Additional parameters to pass to the component's constructor. + /// An instance of the specified component type. + public static T CreateInstance(IHttpServerContext httpServerContext, params object[] advancedParameters) where T : class, IComponentHub + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = typeof(T).GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var parameters = constructor.GetParameters(); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : + + advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + .FirstOrDefault() ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is T component) + { + return component; + } + } + } + + return Activator.CreateInstance(typeof(T), advancedParameters) as T; + } + /// /// Creates an instance of the specified component type with the provided context, component hub advanced parameters. /// diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 253639d..d24aa21 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -205,7 +205,7 @@ public class ComponentHub : IComponentHub /// Initializes a new instance of the class. /// /// The reference to the context of the host. - internal ComponentHub(IHttpServerContext httpServerContext) + protected ComponentHub(IHttpServerContext httpServerContext) { _httpServerContext = httpServerContext; @@ -409,7 +409,7 @@ internal void BootComponent(IEnumerable pluginContexts) /// /// Starts the component. /// - internal void Execute() + public void Execute() { _httpServerContext.Log.Debug ( @@ -423,7 +423,7 @@ internal void Execute() /// /// Shutting down the component manager. /// - internal void ShutDown() + public void ShutDown() { _httpServerContext.Log.Debug ( diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index c1cc45f..82a983a 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -169,5 +169,15 @@ public interface IComponentHub : IComponentManager /// The component class. /// The instance of the component. T GetComponent() where T : IComponentManager; + + /// + /// Starts the component. + /// + void Execute(); + + /// + /// Shutting down the component manager. + /// + void ShutDown(); } } diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 20e210b..9f50c67 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -16,12 +16,29 @@ namespace WebExpress.WebCore { + /// + /// The class provides a web server application for WebExpress with a default component hub. + /// + public class WebEx : WebEx + { + /// + /// Creates and returns a new instance of . + /// + /// The HTTP server context used to initialize the component manager. + /// A new instance of . + protected override IComponentHub CreateComponentManager(IHttpServerContext httpServerContext) + { + return ComponentActivator.CreateInstance(httpServerContext); + } + } + /// /// The class provides a web server application for WebExpress. /// - public class WebEx + /// The type of the component hub, which must implement . + public abstract class WebEx where T : class, IComponentHub { - private static ComponentHub _componentHub; + private static T _componentHub; private HttpServer _httpServer; /// @@ -37,22 +54,7 @@ public class WebEx /// /// Returns the component hub. /// - public static IComponentHub ComponentHub => _componentHub; - - /// - /// Entry point of application. - /// - /// Call arguments. - /// The return code. 0 on success. A number greater than 0 for errors. - public static int Main(string[] args) - { - var app = new WebEx() - { - Name = Assembly.GetExecutingAssembly().GetName().Name - }; - - return app.Execution(args); - } + public static T ComponentHub => _componentHub; /// /// Running the application. @@ -215,7 +217,7 @@ private void Initialization(string args, string configFile) Config = config }; - _componentHub = CreateComponentManager(_httpServer.HttpServerContext); + _componentHub = CreateComponentManager(_httpServer.HttpServerContext) as T; // start logging _httpServer.HttpServerContext.Log.Begin(config.Log); @@ -311,13 +313,10 @@ public static T GetComponent() where T : IComponentManager } /// - /// Creates and returns a new instance of . + /// Creates and returns a new instance of . /// /// The HTTP server context used to initialize the component manager. - /// A new instance of . - protected virtual ComponentHub CreateComponentManager(IHttpServerContext httpServerContext) - { - return new ComponentHub(httpServerContext); - } + /// A new instance of . + protected abstract T CreateComponentManager(IHttpServerContext httpServerContext); } } From 66775b92bbc51997d152ec93227ad8fb69041d38 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 7 Dec 2024 23:01:16 +0100 Subject: [PATCH 055/162] refactoring --- src/WebExpress.WebCore/WebComponent/IComponentHub.cs | 10 ---------- src/WebExpress.WebCore/WebEx.cs | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index 82a983a..c1cc45f 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -169,15 +169,5 @@ public interface IComponentHub : IComponentManager /// The component class. /// The instance of the component. T GetComponent() where T : IComponentManager; - - /// - /// Starts the component. - /// - void Execute(); - - /// - /// Shutting down the component manager. - /// - void ShutDown(); } } diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 9f50c67..d1325cc 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -136,7 +136,7 @@ public int Execution(string[] args) Initialization(ArgumentParser.Current.GetValidArguments(args), Path.Combine(Path.Combine(Environment.CurrentDirectory, "config"), argumentDict["config"])); // start the manager - _componentHub.Execute(); + (_componentHub as ComponentHub).Execute(); // starting the web server Start(); @@ -286,7 +286,7 @@ private void Exit() _httpServer.HttpServerContext.Log.Seperator('/'); // Stop running - _componentHub.ShutDown(); + (_componentHub as ComponentHub).ShutDown(); // stop logging _httpServer.HttpServerContext.Log.Close(); From d1ad9406270bd874729a8c5e306099ebd1383373 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 8 Dec 2024 07:27:27 +0100 Subject: [PATCH 056/162] change return value to IEnumerable of the render method --- .../WebFragment/FragmentManager.cs | 13 +++---------- .../WebFragment/IFragmentManager.cs | 4 ++-- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 5ee6bf9..31cdb98 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -448,8 +448,8 @@ public IEnumerable GetFragments(IApplicationContext applicatio /// /// The context in which rendering occurs. /// The section where the fragment is embedded. - /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - public IHtmlNode Render(IRenderContext renderContext, Type section) + /// An enumeration of HTML nodes representing the rendered fragments. + public IEnumerable Render(IRenderContext renderContext, Type section) { var scopes = renderContext?.PageContext?.Scopes ?? []; @@ -463,14 +463,7 @@ public IHtmlNode Render(IRenderContext renderContext, Type section) .SelectMany(x => x.Value) .OrderBy(x => x.Order); - var nodes = items.Select(x => x.Render(renderContext)).ToList(); - - return nodes.Count switch - { - 0 => null, - 1 => nodes.First(), - _ => new HtmlElementTextContentDiv(nodes) - }; + return items.Select(x => x.Render(renderContext)); } /// diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index a3bbba2..8ce9506 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -91,7 +91,7 @@ public interface IFragmentManager : IComponentManager /// /// The context in which rendering occurs. /// The section where the fragment is embedded. - /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - IHtmlNode Render(IRenderContext renderContext, Type section); + /// An enumeration of HTML nodes representing the rendered fragments. + IEnumerable Render(IRenderContext renderContext, Type section); } } From 306b4b90336bf6e27570576e9bc8c4b98c9770cd Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 8 Dec 2024 13:13:00 +0100 Subject: [PATCH 057/162] refactoring --- .../Html/UnitTestHtmlElementFieldLabel.cs | 3 ++ .../Html/UnitTestHtmlElementTextContentP.cs | 3 ++ .../Html/UnitTestHtmlImage.cs | 23 ++++++++++++ .../Html/UnitTestHtmlText.cs | 3 ++ .../Manager/UnitTestStatusPageManager.cs | 4 +-- src/WebExpress.WebCore/WebHtml/HtmlElement.cs | 35 ++++++++++--------- src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs | 3 ++ 7 files changed, 55 insertions(+), 19 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Html/UnitTestHtmlImage.cs diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs index 400b89d..cd716f1 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementFieldLabel.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.Test.Html { + /// + /// Unit tests for the HtmlElementFieldLabel class. + /// [Collection("NonParallelTests")] public class UnitTestHtmlElementFieldLabel { diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs index 9303a7a..555fdfa 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlElementTextContentP.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.Test.Html { + /// + /// Unit tests for the HtmlElementTextContentP class. + /// [Collection("NonParallelTests")] public class UnitTestHtmlElementTextContentP { diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlImage.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlImage.cs new file mode 100644 index 0000000..f5c9d85 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlImage.cs @@ -0,0 +1,23 @@ +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test.Html +{ + /// + /// Unit tests for the HtmlElementMultimediaImg class. + /// + [Collection("NonParallelTests")] + public class UnitTestHtmlImage + { + /// + /// Tests a empty image. + /// + [Fact] + public void Empty() + { + // test execution + var html = new HtmlElementMultimediaImg(); + + Assert.Equal(@"", html.ToString().Trim()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs index 4096eea..4d499af 100644 --- a/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs +++ b/src/WebExpress.WebCore.Test/Html/UnitTestHtmlText.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.Test.Html { + /// + /// Unit tests for the HtmlText class. + /// [Collection("NonParallelTests")] public class UnitTestHtmlText { diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 9ec5874..199458f 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -165,8 +165,8 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect /// Test the icon property of the status page. /// [Theory] - [InlineData(typeof(TestApplicationA), 400, "content", "content", 78)] - [InlineData(typeof(TestApplicationA), 500, "content", "content", 78)] + [InlineData(typeof(TestApplicationA), 400, "content", "content", 82)] + [InlineData(typeof(TestApplicationA), 500, "content", "content", 82)] public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected, int length) { // preconditions diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs index e56a539..69aad80 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs @@ -17,12 +17,12 @@ public class HtmlElement : IHtmlNode /// /// Returns or sets the attributes. /// - protected List Attributes { get; } = new List(); + protected List Attributes { get; } = []; /// /// Returns or sets the elements. /// - protected List Elements { get; } = new List(); + protected List Elements { get; } = []; /// /// Returns or sets the id. @@ -267,11 +267,11 @@ protected string GetText() /// The call depth. public virtual void ToString(StringBuilder builder, int deep) { - ToPreString(builder, deep); - var closeTag = false; var nl = true; + ToPreString(builder, deep); + if (Elements.Count == 1 && Elements.First() is HtmlText) { closeTag = true; @@ -306,10 +306,10 @@ public virtual void ToString(StringBuilder builder, int deep) } /// - /// Convert to a string using a StringBuilder. + /// Converts the element to a string and appends it to the provided StringBuilder. /// - /// The string builder. - /// The call depth. + /// The StringBuilder to append the string representation to. + /// The depth of the element in the HTML hierarchy, used for indentation. protected virtual void ToPreString(StringBuilder builder, int deep) { if (!Inline) @@ -318,22 +318,23 @@ protected virtual void ToPreString(StringBuilder builder, int deep) builder.Append(string.Empty.PadRight(deep)); } - builder.Append("<"); + builder.Append('<'); builder.Append(ElementName); - foreach (var v in Attributes) + foreach (var attribute in Attributes) { - builder.Append(" "); - v.ToString(builder, 0); + builder.Append(' '); + attribute.ToString(builder, 0); } - builder.Append(">"); + + builder.Append('>'); } /// - /// Convert to a string using a string builder. + /// Converts the element to a string and appends the closing tag to the provided StringBuilder. /// - /// The string builder. - /// The call depth. - /// Start the closing tag on a new line. + /// The StringBuilder to append the string representation to. + /// The depth of the element in the HTML hierarchy, used for indentation. + /// Indicates whether the closing tag should start on a new line. protected virtual void ToPostString(StringBuilder builder, int deep, bool nl = true) { if (!Inline && nl) @@ -344,7 +345,7 @@ protected virtual void ToPostString(StringBuilder builder, int deep, bool nl = t builder.Append(""); + builder.Append('>'); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs b/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs index d0325e5..c7c3a31 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlEmpty.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents an empty HTML node. + /// public class HtmlEmpty : IHtmlNode { /// From 966109e49cf3b83cff6a3f57bb26baa817760c80 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 8 Dec 2024 13:13:55 +0100 Subject: [PATCH 058/162] bug fixes --- .../Manager/UnitTestStatusPageManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 199458f..9ec5874 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -165,8 +165,8 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect /// Test the icon property of the status page. /// [Theory] - [InlineData(typeof(TestApplicationA), 400, "content", "content", 82)] - [InlineData(typeof(TestApplicationA), 500, "content", "content", 82)] + [InlineData(typeof(TestApplicationA), 400, "content", "content", 78)] + [InlineData(typeof(TestApplicationA), 500, "content", "content", 78)] public void CreateAndCheckMessage(Type applicationType, int statusCode, string content, string expected, int length) { // preconditions From 4219d779e6a780b6e5b6ce4538d6700604941eb0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 13 Dec 2024 18:23:17 +0100 Subject: [PATCH 059/162] refactor render process --- .../Manager/UnitTestStatusPageManager.cs | 4 +- src/WebExpress.WebCore.Test/TestPageA.cs | 11 ++-- src/WebExpress.WebCore.Test/TestPageB.cs | 18 ++--- src/WebExpress.WebCore.Test/TestPageC.cs | 11 ++-- .../TestStatusPage301.cs | 11 ++-- .../TestStatusPage400.cs | 13 ++-- .../TestStatusPage404.cs | 11 ++-- .../TestStatusPage500.cs | 13 ++-- src/WebExpress.WebCore/WebLog/Log.cs | 66 ++++++++----------- src/WebExpress.WebCore/WebPage/IPage.cs | 17 ++--- .../WebPage/IRenderContext.cs | 5 -- .../WebPage/Model/PageDictionary.cs | 2 +- .../WebPage/Model/PageItem.cs | 3 +- src/WebExpress.WebCore/WebPage/Page.cs | 9 +-- src/WebExpress.WebCore/WebPage/PageManager.cs | 46 ++++++++----- .../WebPage/RenderContext.cs | 15 ----- .../WebSettingPage/SettingPageManager.cs | 2 +- .../WebStatusPage/IStatusPage.cs | 16 +++-- .../WebStatusPage/StatusPageManager.cs | 43 +++++++----- 19 files changed, 156 insertions(+), 160 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 9ec5874..22765b8 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -142,7 +142,7 @@ public void Icon(Type applicationType, Type statusPageType, string icon) } /// - /// Test the icon property of the status page. + /// Test the CreateStatusResponse function of the status page. /// [Theory] [InlineData(typeof(TestApplicationA), 400, 400)] @@ -162,7 +162,7 @@ public void CreateAndCheckCode(Type applicationType, int statusCode, int? expect } /// - /// Test the icon property of the status page. + /// Test the CreateStatusResponse function of the status page. /// [Theory] [InlineData(typeof(TestApplicationA), 400, "content", "content", 78)] diff --git a/src/WebExpress.WebCore.Test/TestPageA.cs b/src/WebExpress.WebCore.Test/TestPageA.cs index 6c5c918..00392d6 100644 --- a/src/WebExpress.WebCore.Test/TestPageA.cs +++ b/src/WebExpress.WebCore.Test/TestPageA.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:pagea.label")] [Segment("pagea", "webindex:homepage.label")] [ContextPath(null)] - public sealed class TestPageA : IPage + public sealed class TestPageA : IPage { /// /// Returns or sets the title of the page. @@ -39,13 +39,14 @@ public TestPageA(IPageContext pageContext) /// /// Processing of the page. /// - /// The context for rendering the page. - public void Process(IRenderContext context) + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the context - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore.Test/TestPageB.cs b/src/WebExpress.WebCore.Test/TestPageB.cs index 9fe82e0..189796d 100644 --- a/src/WebExpress.WebCore.Test/TestPageB.cs +++ b/src/WebExpress.WebCore.Test/TestPageB.cs @@ -38,25 +38,17 @@ public TestPageB(IPageContext pageContext) } } - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { - - } - /// /// Processing of the page. /// - /// The context for rendering the page. - public void Process(IRenderContext context) + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the context - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore.Test/TestPageC.cs b/src/WebExpress.WebCore.Test/TestPageC.cs index 7a4c39c..aadbeeb 100644 --- a/src/WebExpress.WebCore.Test/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/TestPageC.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:pagec.label")] [Segment(null, "webindex:homepage.label")] [ContextPath(null)] - public sealed class TestPageC : Page + public sealed class TestPageC : Page { /// /// Initialization of the page. Here, for example, managed resources can be loaded. @@ -27,13 +27,14 @@ private TestPageC(IPageContext pageContext) /// /// Processing of the page. /// - /// The context for rendering the page. - public override void Process(IRenderContext context) + /// The context for rendering the page. + /// The visual tree to be rendered. + public override void Process(IRenderContext renderContext, VisualTree visualTree) { // test the context - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore.Test/TestStatusPage301.cs b/src/WebExpress.WebCore.Test/TestStatusPage301.cs index 537cf8e..ad41698 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPage301.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage301.cs @@ -11,7 +11,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:homepage.label")] [StatusResponse()] // [Icon("/webexpress/icon.png")] test empty icon - public sealed class TestStatusPage301 : IStatusPage + public sealed class TestStatusPage301 : IStatusPage { /// /// Initialization of the status page. Here, for example, managed resources can be loaded. @@ -29,13 +29,14 @@ private TestStatusPage301(IStatusPageContext statusPageContext) /// /// Processing of the status page. /// - /// The context for rendering the status page. - public void Process(IRenderContext context) + /// The context for rendering the status page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the parameter - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore.Test/TestStatusPage400.cs b/src/WebExpress.WebCore.Test/TestStatusPage400.cs index 2105240..c005266 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPage400.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage400.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:homepage.label")] [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPage400 : IStatusPage + public sealed class TestStatusPage400 : IStatusPage { /// /// Returns or sets the status message. @@ -44,16 +44,17 @@ private TestStatusPage400(IStatusPageContext statusPageContext, StatusMessage me /// /// Processing of the status page. /// - /// The context for rendering the status page. - public void Process(IRenderContext context) + /// The context for rendering the status page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the parameter - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } - context.VisualTree.Content = new HtmlText(StatusMessage); + visualTree.Content = new HtmlText(StatusMessage); } /// diff --git a/src/WebExpress.WebCore.Test/TestStatusPage404.cs b/src/WebExpress.WebCore.Test/TestStatusPage404.cs index 4ad62d7..9d0504d 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPage404.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage404.cs @@ -11,7 +11,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:homepage.label")] [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPage404 : IStatusPage + public sealed class TestStatusPage404 : IStatusPage { /// /// Initialization of the status page. Here, for example, managed resources can be loaded. @@ -29,13 +29,14 @@ private TestStatusPage404(IStatusPageContext statusPageContext) /// /// Processing of the status page. /// - /// The context for rendering the status page. - public void Process(IRenderContext context) + /// The context for rendering the status page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the parameter - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } diff --git a/src/WebExpress.WebCore.Test/TestStatusPage500.cs b/src/WebExpress.WebCore.Test/TestStatusPage500.cs index 9a7360b..f283612 100644 --- a/src/WebExpress.WebCore.Test/TestStatusPage500.cs +++ b/src/WebExpress.WebCore.Test/TestStatusPage500.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:homepage.label")] [StatusResponse()] [Icon("/webexpress/icon.png")] - public sealed class TestStatusPage500 : IStatusPage + public sealed class TestStatusPage500 : IStatusPage { /// /// Returns or sets the status message. @@ -44,16 +44,17 @@ private TestStatusPage500(IStatusPageContext statusPageContext, StatusMessage me /// /// Processing of the status page. /// - /// The context for rendering the status page. - public void Process(IRenderContext context) + /// The context for rendering the status page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the parameter - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } - context.VisualTree.Content = new HtmlText(StatusMessage); + visualTree.Content = new HtmlText(StatusMessage); } /// diff --git a/src/WebExpress.WebCore/WebLog/Log.cs b/src/WebExpress.WebCore/WebLog/Log.cs index 0074713..2c99439 100644 --- a/src/WebExpress.WebCore/WebLog/Log.cs +++ b/src/WebExpress.WebCore/WebLog/Log.cs @@ -33,6 +33,31 @@ namespace WebExpress.WebCore.WebLog /// public class Log : ILog { + /// + /// The directory where the log is created. + /// + private string _path; + + /// + /// The thread that takes care of the cyclic writing in the log file. + /// + private Thread _workerThread; + + /// + /// Constant that determines the further of the separator rows. + /// + private const int _seperatorWidth = 260; + + /// + /// End worker thread lifecycle. + /// + private bool _done = false; + + /// + /// The width of the log entry output in the console. + /// + private readonly int _width = 250; + /// /// Returns or sets the encoding. /// @@ -88,50 +113,11 @@ public class Log : ILog /// public string TimePattern { set; get; } - /// - /// The directory where the log is created. - /// - private string _path; - - /// - /// The thread that takes care of the cyclic writing in the log file. - /// - private Thread _workerThread; - - /// - /// Constant that determines the further of the separator rows. - /// - private const int _seperatorWidth = 260; - - /// - /// End worker thread lifecycle. - /// - private bool _done = false; - /// /// Unsaved entries queue. /// private readonly Queue _queue = new Queue(); - /// - /// Returns the number of characters for log outputs in the console. - /// - private int Width - { - get - { - try - { - return Console.WindowWidth; - } - catch - { - } - - return 250; - } - } - /// /// Initializes a new instance of the class. /// @@ -236,7 +222,7 @@ protected virtual void Add(LogLevel level, string message, [CallerMemberName] st break; } - Console.WriteLine(item.ToString().Length > _seperatorWidth ? item.ToString().Substring(0, _seperatorWidth - 3) + "..." : item.ToString().PadRight(Width, ' ')); + Console.WriteLine(item.ToString().Length > _seperatorWidth ? string.Concat(item.ToString().AsSpan(0, _seperatorWidth - 3), "...") : item.ToString().PadRight(_width, ' ')); Console.ResetColor(); _queue.Enqueue(item); diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index a3d29ef..5a282c9 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -5,20 +5,21 @@ namespace WebExpress.WebCore.WebPage /// /// Defines the contract for a page resource. /// - public interface IPage : IEndpoint + public interface IPage : IPage { - /// - /// Processing of the page. - /// - /// The context for rendering the page. - void Process(IRenderContext context); } /// /// Defines the contract for a page resource that can be rendered using a specific context. /// - /// The type of the render context. - public interface IPage : IPage where T : IRenderContext + /// The type of the visual tree. + public interface IPage : IEndpoint where T : IVisualTree { + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + void Process(IRenderContext renderContext, T visualTree); } } diff --git a/src/WebExpress.WebCore/WebPage/IRenderContext.cs b/src/WebExpress.WebCore/WebPage/IRenderContext.cs index aa62137..08ece95 100644 --- a/src/WebExpress.WebCore/WebPage/IRenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/IRenderContext.cs @@ -16,10 +16,5 @@ public interface IRenderContext /// Returns the request. /// Request Request { get; } - - /// - /// Returns the contents of a page. - /// - IVisualTree VisualTree { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs index a5e6a0e..a0f4e07 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -23,7 +23,7 @@ public bool AddPageItem(IPluginContext pluginContext, IApplicationContext applic { var type = pageItem.PageClass; - if (!typeof(IPage).IsAssignableFrom(type)) + if (type.GetInterface(typeof(IPage<>).Name) == null) { return false; } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 51f199f..60d673f 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage.Model @@ -30,7 +31,7 @@ internal class PageItem : IDisposable /// /// Returns or sets the instance of the page, if the page is cached, otherwise null. /// - public IPage Instance { get; set; } + public IEndpoint Instance { get; set; } /// /// Returns the scope names that provides the resource. The scope name diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index 38ec06e..4ad7a53 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -4,7 +4,7 @@ /// The prototype of a website. /// /// An implementation of the visualization tree. - public abstract class Page : IPage where T : IRenderContext, new() + public abstract class Page : IPage where T : IVisualTree, new() { /// /// Returns or sets the page title. @@ -29,7 +29,7 @@ public Page() /// The function throws the RedirectException. /// /// The uri to redirect to. - public void Redirecting(string uri) + public virtual void Redirecting(string uri) { throw new RedirectException(uri?.ToString()); } @@ -37,8 +37,9 @@ public void Redirecting(string uri) /// /// Processing of the page. /// - /// The context for rendering the page. - public abstract void Process(IRenderContext context); + /// The context for rendering the page. + /// The visual tree to be rendered. + public abstract void Process(IRenderContext renderContext, T visualTree); /// /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 68c82a5..b390240 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; @@ -23,6 +24,7 @@ public class PageManager : IPageManager private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly PageDictionary _dictionary = []; + private static readonly Dictionary _delegateCache = []; /// /// An event that fires when an page is added. @@ -62,28 +64,40 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon EndpointsResolver = () => Pages, HandleRequest = (request, endpontContext) => { - var page = CreatePageInstance(endpontContext as IPageContext); - var pageType = page.GetType(); - var context = default(IRenderContext); + var pageInstance = CreatePageInstance(endpontContext as IPageContext); + var pageType = pageInstance.GetType(); var pageContetx = endpontContext as IPageContext; + var renderContext = new RenderContext(pageContetx, request); + var visualTreeContext = new VisualTreeContext(renderContext); - if (pageType.IsGenericType) + var visualTreeType = pageType.GetInterface(typeof(IPage<>).Name).GetGenericArguments()[0]; + if (!_delegateCache.TryGetValue(pageType, out var del)) { - var typeOfT = pageType.GetGenericArguments()[0]; - var parameters = new object[] { page, endpontContext as IPageContext, request }; + // create and compile the expression + var renderContextParam = Expression.Parameter(typeof(IRenderContext), "renderContext"); + var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var callProzessMethod = Expression.Call + ( + Expression.Constant(pageInstance), + pageType.GetMethod("Process"), + renderContextParam, + visualTreeParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) + .Compile(); - context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; - } - else - { - context = new RenderContext(pageContetx, request); + _delegateCache[pageType] = lambda; + del = lambda; } - page.Process(context); + var visualTreeInstance = Activator.CreateInstance(visualTreeType) as IVisualTree; + + // execute the cached delegate + del.DynamicInvoke(renderContext, visualTreeInstance); return new ResponseOK() { - Content = context.VisualTree.Render(new VisualTreeContext(context)) + Content = visualTreeInstance.Render(visualTreeContext) }; } }; @@ -213,7 +227,7 @@ public IPageContext GetPage(string applicationId, string pageId) /// /// The context used for page creation. /// The created or cached page. - private IPage CreatePageInstance(IPageContext pageContext) + private IEndpoint CreatePageInstance(IPageContext pageContext) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) @@ -222,7 +236,7 @@ private IPage CreatePageInstance(IPageContext pageContext) if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _httpServerContext, _componentHub); if (resourceItem.Cache) { @@ -277,7 +291,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) - .Where(x => x.GetInterface(typeof(IPage).Name) != null)) + .Where(x => x.GetInterface(typeof(IPage<>).Name) != null)) { var id = resourceType.FullName?.ToLower(); var segment = default(ISegmentAttribute); diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index 4f08556..b9f76a7 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -29,17 +29,11 @@ public class RenderContext : IRenderContext /// public CultureInfo Culture => Request?.Culture; - /// - /// Returns the contents of a page. - /// - public IVisualTree VisualTree { get; protected set; } - /// /// Initializes a new instance of the class. /// public RenderContext() { - VisualTree = CreateVisualTree(); } /// @@ -62,14 +56,5 @@ public RenderContext(RenderContext context) : this(context?.PageContext, context?.Request) { } - - /// - /// Creates the visual tree representing the contents of a page. - /// - /// A new instance of the interface. - protected virtual IVisualTree CreateVisualTree() - { - return new VisualTree(); - } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 02dbd9c..92d3c4b 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -80,7 +80,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe return new ResponseOK() { - Content = context.VisualTree.Render(new VisualTreeContext(context)) + //Content = context.VisualTree.Render(new VisualTreeContext(context)) }; } }; diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs index fd4e0b2..9b6158b 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPage.cs @@ -6,20 +6,22 @@ namespace WebExpress.WebCore.WebStatusPage /// /// Interface of the status pages. /// - public interface IStatusPage : IComponent + public interface IStatusPage : IStatusPage { - /// - /// Processing of the status page. - /// - /// The context for rendering the status page. - void Process(IRenderContext context); + } /// /// Defines the contract for a status page resource that can be rendered using a specific context. /// /// The type of the render context. - public interface IStatusPage : IStatusPage where T : IRenderContext + public interface IStatusPage : IComponent where T : IVisualTree { + /// + /// Processing of the status page. + /// + /// The context for rendering the status page. + /// The visual tree to be rendered. + void Process(IRenderContext context, T visualTree); } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 86b662a..1f24dbb 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Reflection; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -23,6 +24,7 @@ public class StatusPageManager : IStatusPageManager, ISystemComponent private readonly IHttpServerContext _httpServerContext; private readonly StatusPageDictionary _dictionary = []; private readonly Dictionary _defaults = []; + private static readonly Dictionary _delegateCache = []; /// /// An event that fires when an status page is added. @@ -106,7 +108,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) - .Where(x => x.GetInterface(typeof(IStatusPage).Name) != null)) + .Where(x => x.GetInterface(typeof(IStatusPage<>).Name) != null)) { var id = new ComponentId(resource.FullName); var statusResponse = typeof(ResponseInternalServerError); @@ -282,7 +284,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon }; } - var instance = ComponentActivator.CreateInstance + var pageInstance = ComponentActivator.CreateInstance ( statusPageItem.StatusPageClass, statusPageItem.StatusPageContext, @@ -290,26 +292,37 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon _componentHub, new StatusMessage(message) ); - var type = instance.GetType(); - var renderContext = default(IRenderContext); + var pageType = pageInstance.GetType(); + var renderContext = new RenderContext(new PageContext(_componentHub.EndpointManager, null, request.Uri, new UriPathSegmentRoot()), request); + var visualTreeContext = new VisualTreeContext(renderContext); - if (type.IsGenericType) + var visualTreeType = pageType.GetInterface(typeof(IStatusPage<>).Name).GetGenericArguments()[0]; + if (!_delegateCache.TryGetValue(pageType, out var del)) { - var typeOfT = type.GetGenericArguments()[0]; - var parameters = new object[] { statusPageItem.PluginContext, request, new List() }; - - renderContext = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; - } - else - { - renderContext = new RenderContext(); + // create and compile the expression + var renderContextParam = Expression.Parameter(typeof(IRenderContext), "renderContext"); + var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var callProzessMethod = Expression.Call + ( + Expression.Constant(pageInstance), + pageType.GetMethod("Process"), + renderContextParam, + visualTreeParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) + .Compile(); + + _delegateCache[pageType] = lambda; + del = lambda; } - instance.Process(renderContext); + var visualTreeInstance = Activator.CreateInstance(visualTreeType) as IVisualTree; + // execute the cached delegate + del.DynamicInvoke(renderContext, visualTreeInstance); var response = ComponentActivator.CreateInstance(statusPageItem.StatusResponse, _httpServerContext, _componentHub, new StatusMessage(message)); - var content = renderContext.VisualTree.Render(new VisualTreeContext(request))?.ToString(); + var content = visualTreeInstance.Render(new VisualTreeContext(request))?.ToString(); response.Content = content; response.Header.ContentLength = content?.Length ?? 0; From 13f09d0bb836e3346469cb48e305cfb5856f9d2c Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 13 Dec 2024 20:25:02 +0100 Subject: [PATCH 060/162] add custom visual tree for testing purposes --- src/WebExpress.WebCore.Test/TestPageC.cs | 10 +- src/WebExpress.WebCore.Test/TestVisualTree.cs | 116 ++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestVisualTree.cs diff --git a/src/WebExpress.WebCore.Test/TestPageC.cs b/src/WebExpress.WebCore.Test/TestPageC.cs index aadbeeb..9c66427 100644 --- a/src/WebExpress.WebCore.Test/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/TestPageC.cs @@ -9,7 +9,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:pagec.label")] [Segment(null, "webindex:homepage.label")] [ContextPath(null)] - public sealed class TestPageC : Page + public sealed class TestPageC : Page { /// /// Initialization of the page. Here, for example, managed resources can be loaded. @@ -29,13 +29,19 @@ private TestPageC(IPageContext pageContext) /// /// The context for rendering the page. /// The visual tree to be rendered. - public override void Process(IRenderContext renderContext, VisualTree visualTree) + public override void Process(IRenderContext renderContext, TestVisualTree visualTree) { // test the context if (renderContext == null) { throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } + + // test the visualTree + if (visualTree == null) + { + throw new ArgumentNullException(nameof(visualTree), "Parameter cannot be null or empty."); + } } /// diff --git a/src/WebExpress.WebCore.Test/TestVisualTree.cs b/src/WebExpress.WebCore.Test/TestVisualTree.cs new file mode 100644 index 0000000..f915706 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestVisualTree.cs @@ -0,0 +1,116 @@ +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A custom visual tree for testing purposes. + /// + public class TestVisualTree : IVisualTree + { + /// + /// Returns the title of the html document. + /// + public string Title { get; set; } + + /// + /// Returns the favicons. + /// + public List Favicons { get; } = []; + + /// + /// Returns the internal stylesheet. + /// + public List Styles { get; } = []; + + /// + /// Returns the links to the java script files to be used, which are inserted in the header. + /// + public List HeaderScriptLinks { get; } = []; + + /// + /// Returns the links to the java script files to be used. + /// + public List ScriptLinks { get; } = []; + + /// + /// Returns the links to the java script files to be used, which are inserted in the header. + /// + public List HeaderScripts { get; } = []; + + /// + /// Returns the links to the java script files to be used. + /// + public IDictionary Scripts { get; } = new Dictionary(); + + /// + /// Returns the links to the css files to be used. + /// + public List CssLinks { get; } = []; + + /// + /// Returns the meta information. + /// + public List> Meta { get; } = []; + + /// + /// Returns or sets the content. + /// + public IHtmlNode Content { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public TestVisualTree() + { + } + + /// + /// Adds or replaces a java script if it exists. + /// + /// The key. + /// The java script code. + public virtual void AddScript(string key, string code) + { + } + + /// + /// Adds a java script. + /// + /// The link of the java script file. + public virtual void AddScriptLink(string url) + { + } + + /// + /// Adds a java script in the header. + /// + /// The link of the java script file. + public virtual void AddHeaderScriptLinks(string url) + { + } + + /// + /// Convert to html. + /// + /// The context for rendering the visual tree. + /// The page as an html tree. + public virtual IHtmlNode Render(IVisualTreeContext context) + { + var html = new HtmlElementRootHtml(); + html.Head.Title = I18N.Translate(context.Request, Title); + html.Head.Favicons = Favicons?.Select(x => new Favicon(x.Url, x.Mediatype)); + html.Head.Styles = Styles; + html.Head.Meta = Meta; + html.Head.Scripts = HeaderScripts; + html.Body.Elements.Add(Content); + html.Body.Scripts = [.. Scripts.Values]; + + html.Head.CssLinks = CssLinks.Where(x => x != null).Select(x => x.ToString()); + html.Head.ScriptLinks = HeaderScriptLinks?.Where(x => x != null).Select(x => x.ToString()); + + return html; + } + } +} From 393832930f67f0b772112abdb1a296411cdbc17b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 13 Dec 2024 20:34:23 +0100 Subject: [PATCH 061/162] reduced complexity of the visual tree interface --- src/WebExpress.WebCore/WebPage/IVisualTree.cs | 72 +------------------ 1 file changed, 1 insertion(+), 71 deletions(-) diff --git a/src/WebExpress.WebCore/WebPage/IVisualTree.cs b/src/WebExpress.WebCore/WebPage/IVisualTree.cs index 412daae..a869e75 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTree.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebHtml; namespace WebExpress.WebCore.WebPage { @@ -8,75 +7,6 @@ namespace WebExpress.WebCore.WebPage /// public interface IVisualTree { - /// - /// Returns the title of the html document. - /// - string Title { get; set; } - - /// - /// Returns the favicon. - /// - List Favicons { get; } - - /// - /// Returns the internal stylesheet. - /// - List Styles { get; } - - /// - /// Returns the links to the java script files to be used, which are inserted in the header. - /// - List HeaderScriptLinks { get; } - - /// - /// Returns the links to the java script files to be used. - /// - List ScriptLinks { get; } - - /// - /// Returns the links to the java script files to be used, which are inserted in the header. - /// - List HeaderScripts { get; } - - /// - /// Returns the links to the java script files to be used. - /// - IDictionary Scripts { get; } - - /// - /// Returns the links to the css files to be used. - /// - List CssLinks { get; } - - /// - /// Returns the meta information. - /// - List> Meta { get; } - - /// - /// Returns or sets the content. - /// - IHtmlNode Content { get; set; } - - /// - /// Adds a java script. - /// - /// The link of the java script file. - void AddScriptLink(string url); - - /// - /// Adds a java script in the header. - /// - /// The link of the java script file. - void AddHeaderScriptLinks(string url); - - /// - /// Adds or replaces a java script if it exists. - /// - /// The key. - /// The java script code. - void AddScript(string key, string code); - /// /// Convert to html. /// From a71e2a45cac72cb908fa0cecd1aff1b369c760ef Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 14 Dec 2024 17:28:16 +0100 Subject: [PATCH 062/162] add template for parameterizable fragments --- .../Manager/UnitTestFragmentManager.cs | 12 +++-- src/WebExpress.WebCore.Test/TestFragmentB.cs | 4 +- src/WebExpress.WebCore.Test/TestFragmentC.cs | 54 +++++++++++++++++++ .../TestRenderContext.cs | 30 +++++++++++ src/WebExpress.WebCore.Test/TestScopeB.cs | 2 +- src/WebExpress.WebCore.Test/TestScopeC.cs | 11 ++++ .../WebFragment/FragmentManager.cs | 18 +++---- .../WebFragment/IFragment.cs | 11 +++- .../WebFragment/IFragmentBase.cs | 9 ++++ .../WebFragment/IFragmentManager.cs | 14 ++--- .../WebFragment/Model/FragmentDictionary.cs | 10 ++-- .../WebFragment/Model/FragmentItem.cs | 33 ++++++++++-- 12 files changed, 171 insertions(+), 37 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestFragmentC.cs create mode 100644 src/WebExpress.WebCore.Test/TestRenderContext.cs create mode 100644 src/WebExpress.WebCore.Test/TestScopeC.cs create mode 100644 src/WebExpress.WebCore/WebFragment/IFragmentBase.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index e923945..053dc9d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -21,7 +21,7 @@ public void Register() var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(9, componentHub.FragmentManager.Fragments.Count()); + Assert.Equal(12, componentHub.FragmentManager.Fragments.Count()); } /// @@ -55,12 +55,18 @@ public void IsIComponentManager() } /// - /// Test the id property of the fragment handler. + /// Test the id property of the fragment. /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] [InlineData(typeof(TestApplicationB), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] [InlineData(typeof(TestApplicationC), typeof(TestFragmentA), "webexpress.webcore.test.testfragmenta")] + [InlineData(typeof(TestApplicationA), typeof(TestFragmentB), "webexpress.webcore.test.testfragmentb")] + [InlineData(typeof(TestApplicationB), typeof(TestFragmentB), "webexpress.webcore.test.testfragmentb")] + [InlineData(typeof(TestApplicationC), typeof(TestFragmentB), "webexpress.webcore.test.testfragmentb")] + [InlineData(typeof(TestApplicationA), typeof(TestFragmentC), "webexpress.webcore.test.testfragmentc")] + [InlineData(typeof(TestApplicationB), typeof(TestFragmentC), "webexpress.webcore.test.testfragmentc")] + [InlineData(typeof(TestApplicationC), typeof(TestFragmentC), "webexpress.webcore.test.testfragmentc")] public void Id(Type applicationType, Type fragmentType, string id) { // preconditions @@ -80,7 +86,7 @@ public void Id(Type applicationType, Type fragmentType, string id) } /// - /// Test the process function of the fragment handler. + /// Test the process function of the fragment. /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA))] diff --git a/src/WebExpress.WebCore.Test/TestFragmentB.cs b/src/WebExpress.WebCore.Test/TestFragmentB.cs index 97e1acb..07cc2d8 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentB.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentB.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.Test [Section()] [Scope] [Order(0)] - public sealed class TestFragmentB : IFragment + public sealed class TestFragmentB : IFragment { /// /// Initialization of the fragment. Here, for example, managed resources can be loaded. @@ -39,7 +39,7 @@ public TestFragmentB(IComponentHub componentHub, IFragmentContext fragmentContex /// /// The context in which rendering occurs. /// An HTML node representing the rendered fragments. - public IHtmlNode Render(IRenderContext renderContext) + public IHtmlNode Render(RenderContext renderContext) { return new HtmlText("TestFragmentB"); } diff --git a/src/WebExpress.WebCore.Test/TestFragmentC.cs b/src/WebExpress.WebCore.Test/TestFragmentC.cs new file mode 100644 index 0000000..91b26ec --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestFragmentC.cs @@ -0,0 +1,54 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test fragment. + /// + [Section()] + [Scope] + [Order(0)] + public sealed class TestFragmentC : IFragment + { + /// + /// Initialization of the fragment. Here, for example, managed resources can be loaded. + /// + /// The component hub. + /// The context of the fragment. + public TestFragmentC(IComponentHub componentHub, IFragmentContext fragmentContext) + { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + + // test the injection + if (fragmentContext == null) + { + throw new ArgumentNullException(nameof(fragmentContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processes the fragments in the specified render context. + /// + /// The context in which rendering occurs. + /// An HTML node representing the rendered fragments. + public IHtmlNode Render(TestRenderContext renderContext) + { + return new HtmlText("TestFragmentC"); + } + + /// + /// Disposes the resources used by the fragment. + /// + public void Dispose() + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestRenderContext.cs b/src/WebExpress.WebCore.Test/TestRenderContext.cs new file mode 100644 index 0000000..1e31ac7 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestRenderContext.cs @@ -0,0 +1,30 @@ +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A custom render context for testing purposes. + /// + public class TestRenderContext : IRenderContext + { + /// + /// Returns the page context. + /// + public IPageContext PageContext { get; protected set; } + + /// + /// Returns the request. + /// + public Request Request { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// >The page context. + /// The request associated with the rendering context. + public TestRenderContext(IPageContext pageContext, Request request) + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestScopeB.cs b/src/WebExpress.WebCore.Test/TestScopeB.cs index 3e699d9..acd08eb 100644 --- a/src/WebExpress.WebCore.Test/TestScopeB.cs +++ b/src/WebExpress.WebCore.Test/TestScopeB.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.Test { /// - /// Test scope A implementing the IScope interface. + /// Test scope B implementing the IScope interface. /// internal class TestScopeA : IScope { diff --git a/src/WebExpress.WebCore.Test/TestScopeC.cs b/src/WebExpress.WebCore.Test/TestScopeC.cs new file mode 100644 index 0000000..9de524c --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestScopeC.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebScope; + +namespace WebExpress.WebCore.Test +{ + /// + /// Test scope C implementing the IScope interface. + /// + internal class TestScopeC : IScope + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 31cdb98..c093276 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -106,16 +106,9 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass && - x.IsSealed && - x.IsPublic && - ( - x.GetInterfaces().Contains(typeof(IFragment)) || - x.GetInterfaces().Contains(typeof(IFragmentDynamic)) - ) - )) + foreach (var fragmentType in assembly.GetTypes() + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(IFragment<>).Name) != null)) { var id = fragmentType.FullName?.ToLower(); var scopes = new List(); @@ -335,12 +328,13 @@ private void OnRemoveApplication(object sender, IApplicationContext e) { Remove(e); } + /// /// Returns all fragment contexts that belong to a given fragment type. /// /// The fragment type. /// An enumeration of the filtered fragment contexts. - public IEnumerable GetFragments() where T : IFragment + public IEnumerable GetFragments() where T : IFragmentBase { return GetFragments(typeof(T)); } @@ -368,7 +362,7 @@ public IEnumerable GetFragments(Type fragmentType) /// The fragment type.. /// The application context. /// An enumeration of the filtered fragment contexts. - public IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragment + public IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragmentBase { return GetFragments(applicationContext, typeof(T)); } diff --git a/src/WebExpress.WebCore/WebFragment/IFragment.cs b/src/WebExpress.WebCore/WebFragment/IFragment.cs index 28566f8..760297e 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragment.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragment.cs @@ -7,13 +7,20 @@ namespace WebExpress.WebCore.WebFragment /// /// Represents a fragment that is a part of a web component. /// - public interface IFragment : IComponent + public interface IFragment : IFragment + { + } + + /// + /// Represents a fragment that is a part of a web component. + /// + public interface IFragment : IComponent, IFragmentBase where T : IRenderContext { /// /// Convert the fragment to HTML. /// /// The context in which the fragment is rendered. /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - IHtmlNode Render(IRenderContext renderContext); + IHtmlNode Render(T renderContext); } } diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs b/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs new file mode 100644 index 0000000..1a0083a --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Defines the base interface for all fragments in the WebExpress framework. + /// + public interface IFragmentBase + { + } +} diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index 8ce9506..9f35788 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -32,9 +32,9 @@ public interface IFragmentManager : IComponentManager /// /// Returns all fragment contexts that belong to a given fragment type. /// - /// The fragment type.. + /// The fragment type. /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments() where T : IFragment; + IEnumerable GetFragments() where TFragment : IFragmentBase; /// /// Returns all fragment contexts that belong to a given fragment type. @@ -46,10 +46,10 @@ public interface IFragmentManager : IComponentManager /// /// Returns all fragment contexts that belong to a given fragment type. /// - /// The fragment type.. + /// The fragment type.. /// The application context. /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragment; + IEnumerable GetFragments(IApplicationContext applicationContext) where TFragment : IFragmentBase; /// /// Returns all fragment contexts that belong to a given fragment type. @@ -62,11 +62,11 @@ public interface IFragmentManager : IComponentManager /// /// Returns all fragment contexts that belong to a given application. /// - /// The section where the fragment is embedded. - /// The scope where the fragment is embedded. + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. /// The application context. /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments(IApplicationContext applicationContext) where S : ISection where T : IScope; + IEnumerable GetFragments(IApplicationContext applicationContext) where TSection : ISection where TScope : IScope; /// /// Returns all fragment contexts that belong to a given application. diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs index 21f1ca2..9f31854 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs @@ -24,7 +24,7 @@ public bool AddFragmentItem(IPluginContext pluginContext, IApplicationContext ap { var type = fragmentItem.FragmentClass; - if (!typeof(IFragment).IsAssignableFrom(type)) + if (type.GetInterface(typeof(IFragment<>).Name) == null) { return false; } @@ -109,12 +109,12 @@ public IEnumerable RemoveFragments(IApplicationContext applica /// /// Returns the fragment items from the dictionary. /// - /// The type of fragment. + /// The type of fragment. /// The application context. /// An IEnumerable of fragment items - public IEnumerable GetFragmentItems(IApplicationContext applicationContext) where T : IFragment + public IEnumerable GetFragmentItems(IApplicationContext applicationContext) where TFragment : IFragmentBase { - return GetFragmentItems(applicationContext, typeof(T)); + return GetFragmentItems(applicationContext, typeof(TFragment)); } /// @@ -125,7 +125,7 @@ public IEnumerable GetFragmentItems(IApplicationContext applica /// An IEnumerable of fragment items public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type fragmentType) { - if (!typeof(IFragment).IsAssignableFrom(fragmentType)) + if (!typeof(IFragment<>).IsAssignableFrom(fragmentType)) { return []; } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index a43d92b..e9c7374 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -15,9 +16,10 @@ namespace WebExpress.WebCore.WebFragment.Model /// internal class FragmentItem : IDisposable { - private IFragment _instance; + private IComponent _instance; private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; + private static readonly Dictionary _delegateCache = []; /// /// Returns the context of the associated plugin. @@ -79,10 +81,10 @@ public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerCon /// /// The context in which rendering occurs. /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - public IHtmlNode Render(IRenderContext renderContext) + public IHtmlNode Render(T renderContext) where T : IRenderContext { var instance = _instance; - instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); if (Cache) { @@ -91,7 +93,28 @@ public IHtmlNode Render(IRenderContext renderContext) if (CheckControl(renderContext)) { - return instance.Render(renderContext); + if (!_delegateCache.TryGetValue(typeof(T), out var del)) + { + // create and compile the expression + var fragmentType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; + var renderContextParam = Expression.Parameter(fragmentType, "renderContext"); + var callProzessMethod = Expression.Call + ( + Expression.Constant(instance), + fragmentType.GetMethod("Render"), + renderContextParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam) + .Compile(); + + _delegateCache[typeof(T)] = lambda; + del = lambda; + } + + // execute the cached delegate + var html = del.DynamicInvoke(renderContext) as IHtmlNode; + + return html; } return null; @@ -102,7 +125,7 @@ public IHtmlNode Render(IRenderContext renderContext) /// /// The context in which checking occurs. /// True if the fragment is active, false otherwise. - private bool CheckControl(IRenderContext renderContext) + private bool CheckControl(T renderContext) where T : IRenderContext { return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); } From e050751f0eaf2e9b2a7525fc80aebd4d2eabddff Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 14 Dec 2024 18:33:05 +0100 Subject: [PATCH 063/162] bug fixes --- .../Manager/UnitTestFragmentManager.cs | 3 ++- .../WebFragment/FragmentManager.cs | 3 ++- .../WebFragment/IFragmentManager.cs | 3 ++- .../WebFragment/Model/FragmentItem.cs | 12 +++++++----- src/WebExpress.WebCore/WebPage/PageManager.cs | 3 ++- .../WebStatusPage/StatusPageManager.cs | 3 ++- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 053dc9d..857e29f 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -1,6 +1,7 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebScope; namespace WebExpress.WebCore.Test.Manager @@ -101,7 +102,7 @@ public void Process(Type applicationType, Type sectionType, Type scopeType) var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); // test execution - var html = componentHub.FragmentManager.Render(renderContext, sectionType); + var html = componentHub.FragmentManager.Render(renderContext, sectionType); Assert.NotNull(html); Assert.NotEmpty(html.ToString()); diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index c093276..fb13908 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -440,10 +440,11 @@ public IEnumerable GetFragments(IApplicationContext applicatio /// /// Converts the fragments to HTML for a given section within the specified render context. /// + /// The type of the render context. /// The context in which rendering occurs. /// The section where the fragment is embedded. /// An enumeration of HTML nodes representing the rendered fragments. - public IEnumerable Render(IRenderContext renderContext, Type section) + public IEnumerable Render(TRenderContext renderContext, Type section) where TRenderContext : IRenderContext { var scopes = renderContext?.PageContext?.Scopes ?? []; diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index 9f35788..5472850 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -89,9 +89,10 @@ public interface IFragmentManager : IComponentManager /// /// Converts the fragments to HTML for a given section within the specified render context. /// + /// The type of the render context. /// The context in which rendering occurs. /// The section where the fragment is embedded. /// An enumeration of HTML nodes representing the rendered fragments. - IEnumerable Render(IRenderContext renderContext, Type section); + IEnumerable Render(TRenderContext renderContext, Type section) where TRenderContext : IRenderContext; } } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index e9c7374..1bbcbaa 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -84,6 +84,7 @@ public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerCon public IHtmlNode Render(T renderContext) where T : IRenderContext { var instance = _instance; + instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); if (Cache) @@ -93,21 +94,22 @@ public IHtmlNode Render(T renderContext) where T : IRenderContext if (CheckControl(renderContext)) { - if (!_delegateCache.TryGetValue(typeof(T), out var del)) + if (!_delegateCache.TryGetValue(FragmentClass, out var del)) { // create and compile the expression - var fragmentType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; - var renderContextParam = Expression.Parameter(fragmentType, "renderContext"); + var renderContextType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; + var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); + var renderMethod = FragmentClass.GetMethod("Render", [renderContextType]); var callProzessMethod = Expression.Call ( Expression.Constant(instance), - fragmentType.GetMethod("Render"), + renderMethod, renderContextParam ); var lambda = Expression.Lambda(callProzessMethod, renderContextParam) .Compile(); - _delegateCache[typeof(T)] = lambda; + _delegateCache[FragmentClass] = lambda; del = lambda; } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index b390240..ff66112 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -76,10 +76,11 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon // create and compile the expression var renderContextParam = Expression.Parameter(typeof(IRenderContext), "renderContext"); var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var processMethod = pageType.GetMethod("Process", [typeof(IRenderContext), visualTreeType]); var callProzessMethod = Expression.Call ( Expression.Constant(pageInstance), - pageType.GetMethod("Process"), + processMethod, renderContextParam, visualTreeParam ); diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 1f24dbb..cef3228 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -302,10 +302,11 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon // create and compile the expression var renderContextParam = Expression.Parameter(typeof(IRenderContext), "renderContext"); var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var processMethod = pageType.GetMethod("Process", [typeof(IRenderContext), visualTreeType]); var callProzessMethod = Expression.Call ( Expression.Constant(pageInstance), - pageType.GetMethod("Process"), + processMethod, renderContextParam, visualTreeParam ); From 210f9874f2876cd5801980ad9148c8bbeadf2403 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 14 Dec 2024 23:13:03 +0100 Subject: [PATCH 064/162] refactoring --- src/WebExpress.WebCore.Test/TestVisualTree.cs | 2 +- src/WebExpress.WebCore/WebHtml/HtmlElement.cs | 850 +++++++++--------- .../WebHtml/HtmlElementEditDel.cs | 4 +- .../WebHtml/HtmlElementEditIns.cs | 4 +- .../WebHtml/HtmlElementEmbeddedEmbed.cs | 14 +- .../WebHtml/HtmlElementEmbeddedIframe.cs | 14 +- .../WebHtml/HtmlElementEmbeddedObject.cs | 14 +- .../WebHtml/HtmlElementEmbeddedPicture.cs | 14 +- .../WebHtml/HtmlElementFieldButton.cs | 21 +- .../WebHtml/HtmlElementFieldInput.cs | 2 +- .../WebHtml/HtmlElementFieldLabel.cs | 4 +- .../WebHtml/HtmlElementFieldLegend.cs | 2 +- .../WebHtml/HtmlElementFieldSelect.cs | 4 +- .../WebHtml/HtmlElementFormDatalist.cs | 4 +- .../WebHtml/HtmlElementFormFieldset.cs | 4 +- .../WebHtml/HtmlElementFormForm.cs | 4 +- .../WebHtml/HtmlElementFormKeygen.cs | 2 +- .../WebHtml/HtmlElementFormMeter.cs | 2 +- .../WebHtml/HtmlElementFormOptgroup.cs | 4 +- .../WebHtml/HtmlElementFormOption.cs | 4 +- .../WebHtml/HtmlElementFormOutput.cs | 2 +- .../WebHtml/HtmlElementFormProgress.cs | 2 +- .../WebHtml/HtmlElementFormTextarea.cs | 4 +- .../WebHtml/HtmlElementInteractiveCommand.cs | 14 +- .../WebHtml/HtmlElementInteractiveDetails.cs | 14 +- .../WebHtml/HtmlElementInteractiveMenu.cs | 14 +- .../WebHtml/HtmlElementInteractiveSummary.cs | 14 +- .../WebHtml/HtmlElementMultimediaMath.cs | 14 +- .../WebHtml/HtmlElementMultimediaSvg.cs | 14 +- .../WebHtml/HtmlElementScriptingNoscript.cs | 14 +- .../WebHtml/HtmlElementSectionAddress.cs | 14 +- .../WebHtml/HtmlElementSectionArticle.cs | 14 +- .../WebHtml/HtmlElementSectionAside.cs | 14 +- .../WebHtml/HtmlElementSectionBody.cs | 4 +- .../WebHtml/HtmlElementSectionFooter.cs | 16 +- .../WebHtml/HtmlElementSectionH1.cs | 4 +- .../WebHtml/HtmlElementSectionH2.cs | 4 +- .../WebHtml/HtmlElementSectionH3.cs | 4 +- .../WebHtml/HtmlElementSectionH4.cs | 4 +- .../WebHtml/HtmlElementSectionH5.cs | 4 +- .../WebHtml/HtmlElementSectionH6.cs | 4 +- .../WebHtml/HtmlElementSectionHeader.cs | 14 +- .../WebHtml/HtmlElementSectionMain.cs | 14 +- .../WebHtml/HtmlElementSectionNav.cs | 14 +- .../WebHtml/HtmlElementSectionSection.cs | 14 +- .../WebHtml/HtmlElementTableCaption.cs | 4 +- .../WebHtml/HtmlElementTableTable.cs | 2 +- .../WebHtml/HtmlElementTableTbody.cs | 16 +- .../WebHtml/HtmlElementTableTd.cs | 26 +- .../WebHtml/HtmlElementTableTfoot.cs | 16 +- .../WebHtml/HtmlElementTableTh.cs | 16 +- .../WebHtml/HtmlElementTableThead.cs | 16 +- .../WebHtml/HtmlElementTableTr.cs | 16 +- .../HtmlElementTextContentBlockquote.cs | 14 +- .../WebHtml/HtmlElementTextContentDd.cs | 14 +- .../WebHtml/HtmlElementTextContentDiv.cs | 14 +- .../WebHtml/HtmlElementTextContentDl.cs | 14 +- .../WebHtml/HtmlElementTextContentDt.cs | 14 +- .../HtmlElementTextContentFigcaption.cs | 14 +- .../WebHtml/HtmlElementTextContentFigure.cs | 14 +- .../WebHtml/HtmlElementTextContentLi.cs | 14 +- .../WebHtml/HtmlElementTextContentOl.cs | 4 +- .../WebHtml/HtmlElementTextContentP.cs | 4 +- .../WebHtml/HtmlElementTextContentPre.cs | 14 +- .../WebHtml/HtmlElementTextContentUl.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsA.cs | 16 +- .../WebHtml/HtmlElementTextSemanticsAbbr.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsB.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsBdi.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsBdo.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsCite.cs | 16 +- .../WebHtml/HtmlElementTextSemanticsCode.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsData.cs | 16 +- .../WebHtml/HtmlElementTextSemanticsDfn.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsEm.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsI.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsKdb.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsMark.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsQ.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsRp.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsRt.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsRuby.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsS.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsSamp.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsSmall.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsSpan.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsStrong.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsSub.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsSup.cs | 14 +- .../WebHtml/HtmlElementTextSemanticsTime.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsU.cs | 4 +- .../WebHtml/HtmlElementTextSemanticsVar.cs | 14 +- .../WebHtml/HtmlElementWebComponentsSlot.cs | 14 +- .../HtmlElementWebComponentsTemplate.cs | 14 +- src/WebExpress.WebCore/WebHtml/HtmlList.cs | 41 +- src/WebExpress.WebCore/WebPage/VisualTree.cs | 2 +- 96 files changed, 664 insertions(+), 1152 deletions(-) diff --git a/src/WebExpress.WebCore.Test/TestVisualTree.cs b/src/WebExpress.WebCore.Test/TestVisualTree.cs index f915706..31f2d17 100644 --- a/src/WebExpress.WebCore.Test/TestVisualTree.cs +++ b/src/WebExpress.WebCore.Test/TestVisualTree.cs @@ -104,7 +104,7 @@ public virtual IHtmlNode Render(IVisualTreeContext context) html.Head.Styles = Styles; html.Head.Meta = Meta; html.Head.Scripts = HeaderScripts; - html.Body.Elements.Add(Content); + html.Body.Add(Content); html.Body.Scripts = [.. Scripts.Values]; html.Head.CssLinks = CssLinks.Where(x => x != null).Select(x => x.ToString()); diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs index 69aad80..574a09e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs @@ -1,402 +1,448 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace WebExpress.WebCore.WebHtml -{ - /// - /// The basis of all html elements (see RfC 1866). - /// - public class HtmlElement : IHtmlNode - { - /// - /// Returns or sets the name. des Attributes - /// - protected string ElementName { get; set; } - - /// - /// Returns or sets the attributes. - /// - protected List Attributes { get; } = []; - - /// - /// Returns or sets the elements. - /// - protected List Elements { get; } = []; - - /// - /// Returns or sets the id. - /// - public string Id - { - get => GetAttribute("id"); - set => SetAttribute("id", value); - } - - /// - /// Returns or sets the css class. - /// - public string Class - { - get => GetAttribute("class"); - set => SetAttribute("class", value); - } - - /// - /// Returns or sets the css style. - /// - public string Style - { - get => GetAttribute("style"); - set => SetAttribute("style", value); - } - - /// - /// Returns or sets the role. - /// - public string Role - { - get => GetAttribute("role"); - set => SetAttribute("role", value); - } - - /// - /// Returns or sets the html5 data attribute. - /// - public string DataToggle - { - get => GetAttribute("data-toggle"); - set => SetAttribute("data-toggle", value); - } - - /// - /// Returns or sets the html5 data attribute. - /// - public string DataProvide - { - get => GetAttribute("data-provide"); - set => SetAttribute("data-provide", value); - } - - /// - /// Returns or sets the on click attribute. - /// - public string OnClick - { - get => GetAttribute("onclick"); - set => SetAttribute("onclick", value); - } - - /// - /// Determines whether the element is inline. - /// - public bool Inline { get; set; } - - /// - /// Determines whether the element needs an end tag. - /// e.g.: true =
false =
- ///
- public bool CloseTag { get; protected set; } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - public HtmlElement(string name, bool closeTag = true) - { - ElementName = name; - CloseTag = closeTag; - } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the item. - public HtmlElement(string name, bool closeTag, params IHtml[] nodes) - : this(name, closeTag) - { - foreach (var v in nodes) - { - if (v is HtmlAttribute attr) - { - Attributes.Add(attr); - } - else if (v is HtmlElement element) - { - Elements.Add(element); - } - else if (v is HtmlText text) - { - Elements.Add(text); - } - } - } - - /// - /// Returns the value of an attribute. - /// - /// The attribute name. - /// The value of the attribute. - protected string GetAttribute(string name) - { - var a = Attributes.Where(x => x.Name == name).FirstOrDefault(); - - if (a != null) - { - return a is HtmlAttribute ? (a as HtmlAttribute).Value : string.Empty; - } - - return string.Empty; - } - - /// - /// Checks whether an attribute is set. - /// - /// The attribute name. - /// True if attribute exists, false otherwise. - protected bool HasAttribute(string name) - { - var a = Attributes.Where(x => x.Name == name).FirstOrDefault(); - - return (a != null); - } - - /// - /// Sets the value of an attribute. - /// - /// The attribute name. - /// The value of the attribute. - protected void SetAttribute(string name, string value) - { - var a = Attributes.Where(x => x.Name == name).FirstOrDefault(); - - if (a != null) - { - if (string.IsNullOrWhiteSpace(value)) - { - Attributes.Remove(a); - } - else if (a is HtmlAttribute) - { - (a as HtmlAttribute).Value = value; - } - } - else - { - if (!string.IsNullOrWhiteSpace(value)) - { - Attributes.Add(new HtmlAttribute(name, value)); - } - } - } - - /// - /// Setzt den Wert eines Attributs - /// - /// The attribute name. - protected void SetAttribute(string name) - { - var a = Attributes.Where(x => x.Name == name).FirstOrDefault(); - - if (a == null) - { - Attributes.Add(new HtmlAttributeNoneValue(name)); - } - } - - /// - /// Removes an attribute. - /// - /// The attribute name. - protected void RemoveAttribute(string name) - { - var a = Attributes.Where(x => x.Name == name).FirstOrDefault(); - - if (a != null) - { - Attributes.Remove(a); - } - } - - /// - /// Returns an element based on its name. - /// - /// The element name. - /// The element. - protected HtmlElement GetElement(string name) - { - var a = Elements.Where(x => x is HtmlElement && (x as HtmlElement).ElementName == name).FirstOrDefault(); - - return a as HtmlElement; - } - - /// - /// Sets an element. - /// - /// The element. - protected void SetElement(HtmlElement element) - { - if (element != null) - { - var a = Elements.Where(x => x is HtmlElement && (x as HtmlElement).ElementName == element.ElementName); - - foreach (var v in a) - { - Elements.Remove(v); - } - - Elements.Add(element); - } - } - - /// - /// Returns the text. - /// - /// The text. - protected string GetText() - { - var a = Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value); - - return string.Join(" ", a); - } - - /// - /// Convert to a string using a StringBuilder. - /// - /// The string builder. - /// The call depth. - public virtual void ToString(StringBuilder builder, int deep) - { - var closeTag = false; - var nl = true; - - ToPreString(builder, deep); - - if (Elements.Count == 1 && Elements.First() is HtmlText) - { - closeTag = true; - nl = false; - - Elements.First().ToString(builder, 0); - } - else if (Elements.Count > 0) - { - closeTag = true; - var count = builder.Length; - - foreach (var v in Elements.Where(x => x != null)) - { - v.ToString(builder, deep + 1); - } - - if (count == builder.Length) - { - nl = false; - } - } - else if (Elements.Count == 0) - { - nl = false; - } - - if (closeTag || CloseTag) - { - ToPostString(builder, deep, nl); - } - } - - /// - /// Converts the element to a string and appends it to the provided StringBuilder. - /// - /// The StringBuilder to append the string representation to. - /// The depth of the element in the HTML hierarchy, used for indentation. - protected virtual void ToPreString(StringBuilder builder, int deep) - { - if (!Inline) - { - builder.AppendLine(); - builder.Append(string.Empty.PadRight(deep)); - } - - builder.Append('<'); - builder.Append(ElementName); - foreach (var attribute in Attributes) - { - builder.Append(' '); - attribute.ToString(builder, 0); - } - - builder.Append('>'); - } - - /// - /// Converts the element to a string and appends the closing tag to the provided StringBuilder. - /// - /// The StringBuilder to append the string representation to. - /// The depth of the element in the HTML hierarchy, used for indentation. - /// Indicates whether the closing tag should start on a new line. - protected virtual void ToPostString(StringBuilder builder, int deep, bool nl = true) - { - if (!Inline && nl) - { - builder.AppendLine(); - builder.Append(string.Empty.PadRight(deep)); - } - - builder.Append("'); - } - - /// - /// Sets the value of an user-defined attribute. - /// - /// The attribute name. - /// The value of the attribute. - public void AddUserAttribute(string name, string value) - { - SetAttribute(name, value); - } - - /// - /// Returns the value of an user-defined attribute. - /// - /// The attribute name. - /// The value of the attribute. - public string GetUserAttribute(string name) - { - return GetAttribute(name); - } - - /// - /// Checks if a user-defined attribute is set. - /// - /// The attribute name. - /// True wenn Attribut vorhanden, false sonst - public bool HasUserAttribute(string name) - { - return HasAttribute(name); - } - - /// - /// Removes an user-defined attribute. - /// - /// The attribute name. - protected void RemoveUserAttribute(string name) - { - RemoveAttribute(name); - } - - /// - /// Convert to String. - /// - /// The object as a string. - public override string ToString() - { - var builder = new StringBuilder(); - ToString(builder, 0); - - return builder.ToString(); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WebExpress.WebCore.WebHtml +{ + /// + /// The basis of all html elements (see RfC 1866). + /// + public class HtmlElement : IHtmlNode + { + private readonly List _elements = []; + private readonly List _attributes = []; + + /// + /// Returns or sets the name. des Attributes + /// + protected string ElementName { get; set; } + + /// + /// Returns or sets the attributes. + /// + protected IEnumerable Attributes => _attributes; + + /// + /// Returns the elements. + /// + protected IEnumerable Elements => _elements; + + /// + /// Returns or sets the id. + /// + public string Id + { + get => GetAttribute("id"); + set => SetAttribute("id", value); + } + + /// + /// Returns or sets the css class. + /// + public string Class + { + get => GetAttribute("class"); + set => SetAttribute("class", value); + } + + /// + /// Returns or sets the css style. + /// + public string Style + { + get => GetAttribute("style"); + set => SetAttribute("style", value); + } + + /// + /// Returns or sets the role. + /// + public string Role + { + get => GetAttribute("role"); + set => SetAttribute("role", value); + } + + /// + /// Returns or sets the html5 data attribute. + /// + public string DataToggle + { + get => GetAttribute("data-toggle"); + set => SetAttribute("data-toggle", value); + } + + /// + /// Returns or sets the html5 data attribute. + /// + public string DataProvide + { + get => GetAttribute("data-provide"); + set => SetAttribute("data-provide", value); + } + + /// + /// Returns or sets the on click attribute. + /// + public string OnClick + { + get => GetAttribute("onclick"); + set => SetAttribute("onclick", value); + } + + /// + /// Determines whether the element is inline. + /// + public bool Inline { get; set; } + + /// + /// Determines whether the element needs an end tag. + /// e.g.: true =
false =
+ ///
+ public bool CloseTag { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the item. + public HtmlElement(string name, bool closeTag = true) + { + ElementName = name; + CloseTag = closeTag; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the item. + public HtmlElement(string name, bool closeTag, params IHtml[] nodes) + : this(name, closeTag) + { + foreach (var v in nodes) + { + if (v is HtmlAttribute attr) + { + _attributes.Add(attr); + } + else if (v is HtmlElement element) + { + _elements.Add(element); + } + else if (v is HtmlText text) + { + _elements.Add(text); + } + } + } + + /// + /// Adds one or more elements to the html element. + /// + /// The elements to add. + public void Add(params IHtmlNode[] elements) + { + _elements.AddRange(elements); + } + + /// + /// Adds one or more elements to the beginning of the html element. + /// + /// The elements to add. + public void AddFirst(params IHtmlNode[] elements) + { + _elements.InsertRange(0, elements); + } + /// + /// Adds one or more attributes to the html element. + /// + /// The attributes to add. + public void Add(params IHtmlAttribute[] attributes) + { + _attributes.AddRange(attributes); + } + + /// + /// Clear all elements frrom the html element. + /// + public void Clear() + { + _elements.Clear(); + } + + /// + /// Clear all elements from the html element that match the given predicate. + /// + /// The predicate to match elements. + protected void Clear(Func predicate) + { + _elements.RemoveAll(new Predicate(predicate)); + } + /// + /// Returns the value of an attribute. + /// + /// The attribute name. + /// The value of the attribute. + protected string GetAttribute(string name) + { + var a = _attributes.Where(x => x.Name == name).FirstOrDefault(); + + if (a != null) + { + return a is HtmlAttribute ? (a as HtmlAttribute).Value : string.Empty; + } + + return string.Empty; + } + + /// + /// Checks whether an attribute is set. + /// + /// The attribute name. + /// True if attribute exists, false otherwise. + protected bool HasAttribute(string name) + { + var a = _attributes.Where(x => x.Name == name).FirstOrDefault(); + + return (a != null); + } + + /// + /// Sets the value of an attribute. + /// + /// The attribute name. + /// The value of the attribute. + protected void SetAttribute(string name, string value) + { + var a = _attributes.Where(x => x.Name == name).FirstOrDefault(); + + if (a != null) + { + if (string.IsNullOrWhiteSpace(value)) + { + _attributes.Remove(a); + } + else if (a is HtmlAttribute) + { + (a as HtmlAttribute).Value = value; + } + } + else + { + if (!string.IsNullOrWhiteSpace(value)) + { + _attributes.Add(new HtmlAttribute(name, value)); + } + } + } + + /// + /// Setzt den Wert eines Attributs + /// + /// The attribute name. + protected void SetAttribute(string name) + { + var a = _attributes.Where(x => x.Name == name).FirstOrDefault(); + + if (a == null) + { + _attributes.Add(new HtmlAttributeNoneValue(name)); + } + } + + /// + /// Removes an attribute. + /// + /// The attribute name. + protected void RemoveAttribute(string name) + { + var a = _attributes.Where(x => x.Name == name).FirstOrDefault(); + + if (a != null) + { + _attributes.Remove(a); + } + } + + /// + /// Returns an element based on its name. + /// + /// The element name. + /// The element. + protected HtmlElement GetElement(string name) + { + var a = _elements.Where(x => x is HtmlElement && (x as HtmlElement).ElementName == name).FirstOrDefault(); + + return a as HtmlElement; + } + + /// + /// Sets an element. + /// + /// The element. + protected void SetElement(HtmlElement element) + { + if (element != null) + { + var a = _elements.Where(x => x is HtmlElement && (x as HtmlElement).ElementName == element.ElementName); + + foreach (var v in a) + { + _elements.Remove(v); + } + + _elements.Add(element); + } + } + + /// + /// Returns the text. + /// + /// The text. + protected string GetText() + { + var a = _elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value); + + return string.Join(" ", a); + } + + /// + /// Convert to a string using a StringBuilder. + /// + /// The string builder. + /// The call depth. + public virtual void ToString(StringBuilder builder, int deep) + { + var closeTag = false; + var nl = true; + + ToPreString(builder, deep); + + if (_elements.Count == 1 && Elements.First() is HtmlText) + { + closeTag = true; + nl = false; + + _elements.First().ToString(builder, 0); + } + else if (_elements.Count > 0) + { + closeTag = true; + var count = builder.Length; + + foreach (var v in Elements.Where(x => x != null)) + { + v.ToString(builder, deep + 1); + } + + if (count == builder.Length) + { + nl = false; + } + } + else if (_elements.Count == 0) + { + nl = false; + } + + if (closeTag || CloseTag) + { + ToPostString(builder, deep, nl); + } + } + + /// + /// Converts the element to a string and appends it to the provided StringBuilder. + /// + /// The StringBuilder to append the string representation to. + /// The depth of the element in the HTML hierarchy, used for indentation. + protected virtual void ToPreString(StringBuilder builder, int deep) + { + if (!Inline) + { + builder.AppendLine(); + builder.Append(string.Empty.PadRight(deep)); + } + + builder.Append('<'); + builder.Append(ElementName); + foreach (var attribute in Attributes) + { + builder.Append(' '); + attribute.ToString(builder, 0); + } + + builder.Append('>'); + } + + /// + /// Converts the element to a string and appends the closing tag to the provided StringBuilder. + /// + /// The StringBuilder to append the string representation to. + /// The depth of the element in the HTML hierarchy, used for indentation. + /// Indicates whether the closing tag should start on a new line. + protected virtual void ToPostString(StringBuilder builder, int deep, bool nl = true) + { + if (!Inline && nl) + { + builder.AppendLine(); + builder.Append(string.Empty.PadRight(deep)); + } + + builder.Append("'); + } + + /// + /// Sets the value of an user-defined attribute. + /// + /// The attribute name. + /// The value of the attribute. + public void AddUserAttribute(string name, string value) + { + SetAttribute(name, value); + } + + /// + /// Returns the value of an user-defined attribute. + /// + /// The attribute name. + /// The value of the attribute. + public string GetUserAttribute(string name) + { + return GetAttribute(name); + } + + /// + /// Checks if a user-defined attribute is set. + /// + /// The attribute name. + /// True wenn Attribut vorhanden, false sonst + public bool HasUserAttribute(string name) + { + return HasAttribute(name); + } + + /// + /// Removes an user-defined attribute. + /// + /// The attribute name. + protected void RemoveUserAttribute(string name) + { + RemoveAttribute(name); + } + + /// + /// Convert to String. + /// + /// The object as a string. + public override string ToString() + { + var builder = new StringBuilder(); + ToString(builder, 0); + + return builder.ToString(); + } + } +} diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs index 8e5fe89..dd94171 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEditDel.cs @@ -14,7 +14,7 @@ public class HtmlElementEditDel : HtmlElement, IHtmlElementEdit public string Text { get => string.Join(string.Empty, Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -62,7 +62,7 @@ public HtmlElementEditDel(string text) public HtmlElementEditDel(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs index 3c1d528..94f9f79 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEditIns.cs @@ -14,7 +14,7 @@ public class HtmlElementEditIns : HtmlElement, IHtmlElementEdit public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -62,7 +62,7 @@ public HtmlElementEditIns(string text) public HtmlElementEditIns(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs index 9b2154b..56b5713 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedEmbed.cs @@ -12,7 +12,7 @@ public class HtmlElementEmbeddedEmbed : HtmlElement, IHtmlElementEmbedded /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -30,17 +30,7 @@ public HtmlElementEmbeddedEmbed() public HtmlElementEmbeddedEmbed(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementEmbeddedEmbed(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs index 02d4da3..207a20b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedIframe.cs @@ -11,7 +11,7 @@ public class HtmlElementEmbeddedIframe : HtmlElement, IHtmlElementEmbedded /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -29,17 +29,7 @@ public HtmlElementEmbeddedIframe() public HtmlElementEmbeddedIframe(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementEmbeddedIframe(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs index 0f71a91..17ed0c9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedObject.cs @@ -13,7 +13,7 @@ public class HtmlElementEmbeddedObject : HtmlElement, IHtmlElementEmbedded /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -30,17 +30,7 @@ public HtmlElementEmbeddedObject() public HtmlElementEmbeddedObject(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementEmbeddedObject(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs index 069f1d9..d4f9bf8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedPicture.cs @@ -10,7 +10,7 @@ public class HtmlElementEmbeddedPicture : HtmlElement, IHtmlElementEmbedded /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementEmbeddedPicture() public HtmlElementEmbeddedPicture(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementEmbeddedPicture(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs index 1a71187..0ea7131 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldButton.cs @@ -24,13 +24,13 @@ public string Name public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets a value. @@ -103,17 +103,7 @@ public HtmlElementFieldButton(string text) public HtmlElementFieldButton(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementFieldButton(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } /// @@ -123,11 +113,6 @@ public HtmlElementFieldButton(IEnumerable nodes) /// The call depth. public override void ToString(StringBuilder builder, int deep) { - //if (Type == "submit" || Type == "reset") - //{ - // ElementName = "input"; - //} - base.ToString(builder, deep); } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs index 389120b..af9072f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldInput.cs @@ -196,7 +196,7 @@ public HtmlElementFieldInput() public HtmlElementFieldInput(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs index 27bf088..5b8c75d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs @@ -25,7 +25,7 @@ public string For public string Text { get => string.Join(string.Empty, Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.RemoveAll(x => x is HtmlText); Elements.Insert(0, new HtmlText(value)); } + set { Clear(x => x is HtmlText); AddFirst(new HtmlText(value)); } } /// @@ -54,7 +54,7 @@ public HtmlElementFieldLabel(string text) public HtmlElementFieldLabel(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs index bfd68c7..ac02d69 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs @@ -13,7 +13,7 @@ public class HtmlElementFieldLegend : HtmlElement public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs index 82300e3..13e7d6d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs @@ -15,7 +15,7 @@ public class HtmlElementFieldSelect : HtmlElement, IHtmlElementFormItem /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the name of the input field. @@ -78,7 +78,7 @@ public HtmlElementFieldSelect() public HtmlElementFieldSelect(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs index bccbc46..e9f6a4c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormDatalist.cs @@ -11,7 +11,7 @@ public class HtmlElementFormDatalist : HtmlElement, IHtmlElementForm /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -37,7 +37,7 @@ public HtmlElementFormDatalist(string text) public HtmlElementFormDatalist(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs index 48117ed..243c46a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormFieldset.cs @@ -39,7 +39,7 @@ public string Form /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -57,7 +57,7 @@ public HtmlElementFormFieldset() public HtmlElementFormFieldset(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs index 0fe1444..54c66ce 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormForm.cs @@ -66,7 +66,7 @@ public string Target /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -92,7 +92,7 @@ public HtmlElementFormForm(string text) public HtmlElementFormForm(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs index c836768..c3216ba 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormKeygen.cs @@ -22,7 +22,7 @@ public HtmlElementFormKeygen() public HtmlElementFormKeygen(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs index e6f50d8..d769c9e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormMeter.cs @@ -14,7 +14,7 @@ public class HtmlElementFormMeter : HtmlElement, IHtmlElementForm public string Text { get => string.Join(string.Empty, Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs index fb29ee2..b4ade2f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs @@ -17,7 +17,7 @@ public class HtmlElementFormOptgroup : HtmlElement, IHtmlElementFormItem /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the label. @@ -43,7 +43,7 @@ public HtmlElementFormOptgroup() public HtmlElementFormOptgroup(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs index fa84954..ca8b3c6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs @@ -18,7 +18,7 @@ public class HtmlElementFormOption : HtmlElement, IHtmlElementFormItem public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -54,7 +54,7 @@ public HtmlElementFormOption() public HtmlElementFormOption(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs index 9567bca..393e931 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOutput.cs @@ -22,7 +22,7 @@ public HtmlElementFormOutput() public HtmlElementFormOutput(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs index f30e1b9..4b5f1dc 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormProgress.cs @@ -14,7 +14,7 @@ public class HtmlElementFormProgress : HtmlElement, IHtmlElementForm public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs index c786f77..507d17a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormTextarea.cs @@ -23,7 +23,7 @@ public string Name public string Value { get => string.Join(string.Empty, Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -132,7 +132,7 @@ public HtmlElementFormTextarea() public HtmlElementFormTextarea(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs index bdf0a87..0acad5a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveCommand.cs @@ -10,7 +10,7 @@ public class HtmlElementInteractiveCommand : HtmlElement, IHtmlElementInteractiv /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementInteractiveCommand() public HtmlElementInteractiveCommand(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementInteractiveCommand(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs index cb61985..ff30fe8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveDetails.cs @@ -10,7 +10,7 @@ public class HtmlElementInteractiveDetails : HtmlElement, IHtmlElementInteractiv /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementInteractiveDetails() public HtmlElementInteractiveDetails(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementInteractiveDetails(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs index db8f850..ac3895b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveMenu.cs @@ -10,7 +10,7 @@ public class HtmlElementInteractiveMenu : HtmlElement, IHtmlElementInteractive /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementInteractiveMenu() public HtmlElementInteractiveMenu(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementInteractiveMenu(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs index 282a928..54bf29f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs @@ -10,7 +10,7 @@ public class HtmlElementInteractiveSummary : HtmlElement, IHtmlElementInteractiv /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementInteractiveSummary() public HtmlElementInteractiveSummary(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementInteractiveSummary(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs index d0a1e10..ba0ced0 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMath.cs @@ -10,7 +10,7 @@ public class HtmlElementMultimediaMath : HtmlElement, IHtmlElementMultimedia /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementMultimediaMath() public HtmlElementMultimediaMath(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementMultimediaMath(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs index 283e5f8..1a904a1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaSvg.cs @@ -11,7 +11,7 @@ public class HtmlElementMultimediaSvg : HtmlElement, IHtmlElementMultimedia /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the width. @@ -55,17 +55,7 @@ public HtmlElementMultimediaSvg() public HtmlElementMultimediaSvg(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementMultimediaSvg(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs index 7559f63..7832e49 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementScriptingNoscript.cs @@ -10,7 +10,7 @@ public class HtmlElementScriptingNoscript : HtmlElement, IHtmlElementScripting /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementScriptingNoscript() public HtmlElementScriptingNoscript(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementScriptingNoscript(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs index 1481aa8..9b6ad27 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAddress.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionAddress : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementSectionAddress() public HtmlElementSectionAddress(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionAddress(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs index 4b3d40d..ec7c9d5 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionArticle.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionArticle : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementSectionArticle() public HtmlElementSectionArticle(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionArticle(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs index 51c3077..3b2528c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionAside.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionAside : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementSectionAside() public HtmlElementSectionAside(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionAside(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs index ebf58f6..0ee49c0 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs @@ -12,7 +12,7 @@ public class HtmlElementSectionBody : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the script elements. @@ -59,7 +59,7 @@ public HtmlElementSectionBody() public HtmlElementSectionBody(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs index 6c4b4df..6499a17 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionFooter.cs @@ -11,7 +11,7 @@ public class HtmlElementSectionFooter : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the text. @@ -19,7 +19,7 @@ public class HtmlElementSectionFooter : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -47,17 +47,7 @@ public HtmlElementSectionFooter(string text) public HtmlElementSectionFooter(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionFooter(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs index 7f5b196..1f291b4 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH1.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH1 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH1(string text) public HtmlElementSectionH1(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs index 804a42c..f5e5036 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH2.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH2 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH2(string text) public HtmlElementSectionH2(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs index ea61c66..78d2c5a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH3.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH3 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH3(string text) public HtmlElementSectionH3(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs index 1597f78..d92e0e5 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH4.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH4 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH4(string text) public HtmlElementSectionH4(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs index bc87c0f..15728b4 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH5.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH5 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH5(string text) public HtmlElementSectionH5(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs index 0ef3d64..5feac4b 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionH6.cs @@ -14,7 +14,7 @@ public class HtmlElementSectionH6 : HtmlElement, IHtmlElementSection public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementSectionH6(string text) public HtmlElementSectionH6(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs index e998cd6..8315f35 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionHeader.cs @@ -11,7 +11,7 @@ public class HtmlElementSectionHeader : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -29,17 +29,7 @@ public HtmlElementSectionHeader() public HtmlElementSectionHeader(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionHeader(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs index 57fd2be..c0e092a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionMain : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementSectionMain() public HtmlElementSectionMain(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionMain(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs index a77e8b7..737d2fa 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionNav.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionNav : HtmlElement /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementSectionNav() public HtmlElementSectionNav(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionNav(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs index 2814246..406e4b9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionSection.cs @@ -10,7 +10,7 @@ public class HtmlElementSectionSection : HtmlElement, IHtmlElementSection /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementSectionSection() public HtmlElementSectionSection(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementSectionSection(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs index f4d1f50..88a60e1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableCaption.cs @@ -14,7 +14,7 @@ public class HtmlElementTableCaption : HtmlElement, IHtmlElementTable public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -42,7 +42,7 @@ public HtmlElementTableCaption(string text) public HtmlElementTableCaption(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs index 9f134d8..00afad9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTable.cs @@ -40,7 +40,7 @@ public override void ToString(StringBuilder builder, int deep) var column = new HtmlElementTableThead(Columns); column.ToString(builder, deep + 1); - var body = new HtmlElementTableTbody(Rows); + var body = new HtmlElementTableTbody(Rows.ToArray()); body.ToString(builder, deep + 1); ToPostString(builder, deep); diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs index a535232..179e159 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTbody.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents the columns that contain the actual data of a table. @@ -22,17 +20,7 @@ public HtmlElementTableTbody() public HtmlElementTableTbody(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTbody(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs index 7d99e13..c41d88e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTd.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents a single table cell. @@ -15,16 +13,6 @@ public HtmlElementTableTd() { } - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTd(IHtmlNode node) - : this() - { - Elements.Add(node); - } - /// /// Initializes a new instance of the class. /// @@ -32,17 +20,7 @@ public HtmlElementTableTd(IHtmlNode node) public HtmlElementTableTd(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTd(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs index d09a356..578ebe9 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTfoot.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents the group of table rows that contain the summaries of the table columns. @@ -22,17 +20,7 @@ public HtmlElementTableTfoot() public HtmlElementTableTfoot(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTfoot(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs index 04f6d39..b09f878 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTh.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents a table cell with a label. @@ -22,17 +20,7 @@ public HtmlElementTableTh() public HtmlElementTableTh(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTh(List nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs index a69822d..4b12ebd 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableThead.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents the group of table rows that contain the labels of the table columns. @@ -22,17 +20,7 @@ public HtmlElementTableThead() public HtmlElementTableThead(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableThead(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs index fae7d47..68be008 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTableTr.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebHtml +namespace WebExpress.WebCore.WebHtml { /// /// Represents a row of table cells. @@ -22,17 +20,7 @@ public HtmlElementTableTr() public HtmlElementTableTr(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTableTr(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs index 6e258dd..60fbaf3 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentBlockquote.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentBlockquote : HtmlElement, IHtmlElementTextCon /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentBlockquote() public HtmlElementTextContentBlockquote(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentBlockquote(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs index 6cd2b7c..9732031 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentDd : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextContentDd() public HtmlElementTextContentDd(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentDd(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs index 25215ef..fbe6fc0 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDiv.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentDiv : HtmlElement /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextContentDiv() public HtmlElementTextContentDiv(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentDiv(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs index 05e793e..0cc5333 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDl.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentDl : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextContentDl() public HtmlElementTextContentDl(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentDl(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs index 58a6f77..6669fcc 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentDt : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentDt() public HtmlElementTextContentDt(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentDt(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs index 9861bde..6ed56d2 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigcaption.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentFigcaption : HtmlElement, IHtmlElementTextCon /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentFigcaption() public HtmlElementTextContentFigcaption(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentFigcaption(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs index 653e752..a626abd 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentFigure.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentFigure : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentFigure() public HtmlElementTextContentFigure(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentFigure(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs index e5cb6c8..66e4668 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentLi.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentLi : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentLi() public HtmlElementTextContentLi(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentLi(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs index 27aec2a..981987f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs @@ -40,8 +40,8 @@ public HtmlElementTextContentOl(params HtmlElementTextContentLi[] nodes) /// The call depth. public override void ToString(StringBuilder builder, int deep) { - base.Elements.Clear(); - base.Elements.AddRange(Elements); + //base.Elements.Clear(); + //base.Elements.AddRange(Elements); base.ToString(builder, deep); } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs index 356ec72..2f55435 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentP.cs @@ -14,7 +14,7 @@ public class HtmlElementTextContentP : HtmlElement, IHtmlElementTextContent public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementTextContentP(string text) public HtmlElementTextContentP(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs index b1f5976..8a2cf35 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentPre.cs @@ -10,7 +10,7 @@ public class HtmlElementTextContentPre : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextContentPre() public HtmlElementTextContentPre(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentPre(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs index e088ccf..9be3229 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentUl.cs @@ -11,7 +11,7 @@ public class HtmlElementTextContentUl : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextContentUl() public HtmlElementTextContentUl(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextContentUl(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs index ac2aa95..025bb3e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsA.cs @@ -12,7 +12,7 @@ public class HtmlElementTextSemanticsA : HtmlElement, IHtmlElementTextSemantics /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the text. @@ -20,7 +20,7 @@ public class HtmlElementTextSemanticsA : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -85,17 +85,7 @@ public HtmlElementTextSemanticsA(string text) public HtmlElementTextSemanticsA(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsA(IEnumerable nodes) - : this() - { - Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs index dc27959..132798f 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsAbbr.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsAbbr : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsAbbr() public HtmlElementTextSemanticsAbbr(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsAbbr(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs index 3e9f491..b25f1d7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsB.cs @@ -15,7 +15,7 @@ public class HtmlElementTextSemanticsB : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -44,7 +44,7 @@ public HtmlElementTextSemanticsB(string text) public HtmlElementTextSemanticsB(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs index 7220fe6..124f8b6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdi.cs @@ -15,7 +15,7 @@ public class HtmlElementTextSemanticsBdi : HtmlElement, IHtmlElementTextSemantic public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -53,7 +53,7 @@ public HtmlElementTextSemanticsBdi(string text) public HtmlElementTextSemanticsBdi(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs index f6ba5ff..001135e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsBdo.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsBdo : HtmlElement, IHtmlElementTextSemantic public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -52,7 +52,7 @@ public HtmlElementTextSemanticsBdo(string text) public HtmlElementTextSemanticsBdo(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs index 44660a5..e81cb14 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCite.cs @@ -14,13 +14,13 @@ public class HtmlElementTextSemanticsCite : HtmlElement, IHtmlElementTextSemanti public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -48,17 +48,7 @@ public HtmlElementTextSemanticsCite(string text) public HtmlElementTextSemanticsCite(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsCite(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs index 37ac379..fdc6949 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsCode.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsCode : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextSemanticsCode() public HtmlElementTextSemanticsCode(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsCode(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs index 87111eb..323de06 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsData.cs @@ -12,7 +12,7 @@ public class HtmlElementTextSemanticsData : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Returns or sets the value. @@ -20,7 +20,7 @@ public class HtmlElementTextSemanticsData : HtmlElement, IHtmlElementTextSemanti public string Value { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -38,17 +38,7 @@ public HtmlElementTextSemanticsData() public HtmlElementTextSemanticsData(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsData(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs index f51f335..23bdc81 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsDfn.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsDfn : HtmlElement, IHtmlElementTextSemantic /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsDfn() public HtmlElementTextSemanticsDfn(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsDfn(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs index 23b0fc6..ee65423 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsEm.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsEm : HtmlElement, IHtmlElementTextSemantics /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextSemanticsEm() public HtmlElementTextSemanticsEm(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsEm(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs index 901cb42..54360c4 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsI.cs @@ -17,7 +17,7 @@ public class HtmlElementTextSemanticsI : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -45,7 +45,7 @@ public HtmlElementTextSemanticsI(string text) public HtmlElementTextSemanticsI(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs index 86e8609..4b56b20 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsKdb.cs @@ -11,7 +11,7 @@ public class HtmlElementTextSemanticsKdb : HtmlElement, IHtmlElementTextSemantic /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextSemanticsKdb() public HtmlElementTextSemanticsKdb(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsKdb(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs index d4a7d66..f89cab1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsMark.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsMark : HtmlElement, IHtmlElementTextSemanti public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsMark(string text) public HtmlElementTextSemanticsMark(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs index e95ece0..0df39f7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsQ : HtmlElement, IHtmlElementTextSemantics /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsQ() public HtmlElementTextSemanticsQ(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsQ(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs index 0391e99..0e3a5d8 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs @@ -15,7 +15,7 @@ public class HtmlElementTextSemanticsRp : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -43,7 +43,7 @@ public HtmlElementTextSemanticsRp(string text) public HtmlElementTextSemanticsRp(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs index 32a9ade..441f9c7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRt.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsRt : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsRt(string text) public HtmlElementTextSemanticsRt(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs index b84f3d8..fb031f7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRuby.cs @@ -11,7 +11,7 @@ public class HtmlElementTextSemanticsRuby : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextSemanticsRuby() public HtmlElementTextSemanticsRuby(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsRuby(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs index 203bee5..a148deb 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsS.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsS : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsS(string text) public HtmlElementTextSemanticsS(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs index 5a80e4e..2e2083a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSamp.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsSamp : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -28,17 +28,7 @@ public HtmlElementTextSemanticsSamp() public HtmlElementTextSemanticsSamp(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsSamp(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs index b258aec..0ce07ca 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSmall.cs @@ -15,7 +15,7 @@ public class HtmlElementTextSemanticsSmall : HtmlElement, IHtmlElementTextSemant public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -44,7 +44,7 @@ public HtmlElementTextSemanticsSmall(string text) public HtmlElementTextSemanticsSmall(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs index 0960230..f265921 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSpan.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsSpan : HtmlElement, IHtmlElementTextSemanti /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsSpan() public HtmlElementTextSemanticsSpan(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsSpan(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs index 1eaff7c..04fc5c6 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsStrong.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsStrong : HtmlElement, IHtmlElementTextSeman public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -42,7 +42,7 @@ public HtmlElementTextSemanticsStrong(string text) public HtmlElementTextSemanticsStrong(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs index 43a740e..c930ebb 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSub.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsSub : HtmlElement, IHtmlElementTextSemantic /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsSub() public HtmlElementTextSemanticsSub(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsSub(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs index a344f4e..adce2e4 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsSup.cs @@ -10,7 +10,7 @@ public class HtmlElementTextSemanticsSup : HtmlElement, IHtmlElementTextSemantic /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementTextSemanticsSup() public HtmlElementTextSemanticsSup(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsSup(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs index 7f1a825..5756c1d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsTime.cs @@ -14,7 +14,7 @@ public class HtmlElementTextSemanticsTime : HtmlElement, IHtmlElementTextSemanti public string Time { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -32,7 +32,7 @@ public HtmlElementTextSemanticsTime() public HtmlElementTextSemanticsTime(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs index 21c2673..9905710 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsU.cs @@ -16,7 +16,7 @@ public class HtmlElementTextSemanticsU : HtmlElement, IHtmlElementTextSemantics public string Text { get => string.Join("", Elements.Where(x => x is HtmlText).Select(x => (x as HtmlText).Value)); - set { Elements.Clear(); Elements.Add(new HtmlText(value)); } + set { Clear(); Add(new HtmlText(value)); } } /// @@ -44,7 +44,7 @@ public HtmlElementTextSemanticsU(string text) public HtmlElementTextSemanticsU(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs index 74e964a..8ae59b1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsVar.cs @@ -12,7 +12,7 @@ public class HtmlElementTextSemanticsVar : HtmlElement, IHtmlElementTextSemantic /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -29,17 +29,7 @@ public HtmlElementTextSemanticsVar() public HtmlElementTextSemanticsVar(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementTextSemanticsVar(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs index 7df295d..64aef21 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsSlot.cs @@ -10,7 +10,7 @@ public class HtmlElementWebFragmentsSlot : HtmlElement, IHtmlElementWebFragments /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementWebFragmentsSlot() public HtmlElementWebFragmentsSlot(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementWebFragmentsSlot(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs index b3b32f2..ac7741c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementWebComponentsTemplate.cs @@ -10,7 +10,7 @@ public class HtmlElementWebFragmentsTemplate : HtmlElement, IHtmlElementWebFragm /// /// Returns the elements. /// - public new List Elements => base.Elements; + public new IEnumerable Elements => base.Elements; /// /// Initializes a new instance of the class. @@ -27,17 +27,7 @@ public HtmlElementWebFragmentsTemplate() public HtmlElementWebFragmentsTemplate(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); - } - - /// - /// Initializes a new instance of the class. - /// - /// The content of the html element. - public HtmlElementWebFragmentsTemplate(IEnumerable nodes) - : this() - { - base.Elements.AddRange(nodes); + Add(nodes); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlList.cs b/src/WebExpress.WebCore/WebHtml/HtmlList.cs index 62f7983..365a73c 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlList.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlList.cs @@ -8,17 +8,18 @@ namespace WebExpress.WebCore.WebHtml /// public class HtmlList : IHtmlNode { + private readonly List _elements = []; + /// /// Returns the elements. /// - public List Elements { get; private set; } + public IEnumerable Elements => _elements; /// /// Initializes a new instance of the class. /// public HtmlList() { - Elements = new List(); } /// @@ -28,7 +29,7 @@ public HtmlList() public HtmlList(params IHtmlNode[] nodes) : this() { - Elements.AddRange(nodes); + _elements.AddRange(nodes); } /// @@ -39,8 +40,8 @@ public HtmlList(params IHtmlNode[] nodes) public HtmlList(IHtmlNode firstNode, params IHtmlNode[] followingNodes) : this() { - Elements.Add(firstNode); - Elements.AddRange(followingNodes); + _elements.Add(firstNode); + _elements.AddRange(followingNodes); } /// @@ -50,7 +51,7 @@ public HtmlList(IHtmlNode firstNode, params IHtmlNode[] followingNodes) public HtmlList(IEnumerable nodes) : this() { - Elements.AddRange(nodes); + _elements.AddRange(nodes); } /// @@ -61,8 +62,17 @@ public HtmlList(IEnumerable nodes) public HtmlList(IHtmlNode firstNode, IEnumerable followingNodes) : this() { - Elements.Add(firstNode); - Elements.AddRange(followingNodes); + _elements.Add(firstNode); + _elements.AddRange(followingNodes); + } + + /// + /// Adds one or more elements to the list. + /// + /// The elements to add. + protected void Add(params IHtmlNode[] elements) + { + _elements.AddRange(elements); } /// @@ -73,10 +83,23 @@ public HtmlList(IHtmlNode firstNode, IEnumerable followingNodes) /// Start the closing tag on a new line. public void ToString(StringBuilder builder, int deep) { - foreach (var v in Elements) + foreach (var v in _elements) { v.ToString(builder, deep); } } + + /// + /// Converts the HTML list to its string representation. + /// + /// A string that represents the HTML list. + public override string ToString() + { + var builder = new StringBuilder(); + + ToString(builder, 0); + + return builder.ToString(); + } } } diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index 6ea27ae..c69c049 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -120,7 +120,7 @@ public virtual IHtmlNode Render(IVisualTreeContext context) html.Head.Styles = Styles; html.Head.Meta = Meta; html.Head.Scripts = HeaderScripts; - html.Body.Elements.Add(Content); + html.Body.Add(Content); html.Body.Scripts = [.. Scripts.Values]; html.Head.CssLinks = CssLinks.Where(x => x != null).Select(x => x.ToString()); From d8bdf6b64c71807819836fc9446f386e3b606ea3 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 10:38:02 +0100 Subject: [PATCH 065/162] add github actions workflow for generating documentation --- .github/workflows/generate-docs.yml | 28 +++++++++++++++++++ .../WebExpress.WebCore.csproj | 1 + 2 files changed, 29 insertions(+) create mode 100644 .github/workflows/generate-docs.yml diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml new file mode 100644 index 0000000..8d56299 --- /dev/null +++ b/.github/workflows/generate-docs.yml @@ -0,0 +1,28 @@ +name: Generate Documentation + +on: + push: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install dependencies + run: | + dotnet restore + + - name: Generate documentation + run: | + dotnet docfx msbuild /p:DocumentationFile=true /p:GenerateDocumentation=true + + - name: Commit and push documentation + run: | + git add -f docs/ + git commit -m "Update documentation" + git push diff --git a/src/WebExpress.WebCore/WebExpress.WebCore.csproj b/src/WebExpress.WebCore/WebExpress.WebCore.csproj index 20d3a02..95844d4 100644 --- a/src/WebExpress.WebCore/WebExpress.WebCore.csproj +++ b/src/WebExpress.WebCore/WebExpress.WebCore.csproj @@ -22,6 +22,7 @@ webexpress True true + true From 27e3a1ae64ddf274f118dc4c9ef36f70d2a745af Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 10:47:51 +0100 Subject: [PATCH 066/162] add empty docs directory with .gitkeep --- docs/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/.gitkeep diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 From d7c6c819b1a2d8397e9fe661c34e3025668e0391 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 10:51:19 +0100 Subject: [PATCH 067/162] fix: specify project directory for dotnet restore in GitHub Actions --- .github/workflows/generate-docs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 8d56299..c94558c 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -13,13 +13,14 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Navigate to project directory + run: cd src/WebExpress.WebCore + - name: Install dependencies - run: | - dotnet restore + run: dotnet restore - name: Generate documentation - run: | - dotnet docfx msbuild /p:DocumentationFile=true /p:GenerateDocumentation=true + run: docfx build - name: Commit and push documentation run: | From cdfb57eb3b647c47d62d5f80ec57c44e346540ca Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 10:57:08 +0100 Subject: [PATCH 068/162] fix: specify project directory for dotnet restore in GitHub Actions --- .github/workflows/generate-docs.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index c94558c..913add5 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -14,12 +14,11 @@ jobs: uses: actions/checkout@v2 - name: Navigate to project directory - run: cd src/WebExpress.WebCore - - - name: Install dependencies + working-directory: src/WebExpress.WebCore run: dotnet restore - name: Generate documentation + working-directory: src/WebExpress.WebCore run: docfx build - name: Commit and push documentation From 0ebdb46dfa910970f84bdb747515975b354d6e97 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:30:18 +0100 Subject: [PATCH 069/162] fix: install .NET 9 SDK in GitHub actions workflow --- .github/workflows/generate-docs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 913add5..14643ae 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -13,6 +13,11 @@ jobs: - name: Checkout code uses: actions/checkout@v2 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '9.0.x' + - name: Navigate to project directory working-directory: src/WebExpress.WebCore run: dotnet restore From f2309c0f25f0251ab30ccb3d94282088a9aa7103 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:33:46 +0100 Subject: [PATCH 070/162] fix: add DocFX installation to GitHub actions workflow --- .github/workflows/generate-docs.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 14643ae..e79e810 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -18,6 +18,12 @@ jobs: with: dotnet-version: '9.0.x' + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Add DocFX to PATH + run: echo "::add-path::$HOME/.dotnet/tools" + - name: Navigate to project directory working-directory: src/WebExpress.WebCore run: dotnet restore From 64137b951dbcac2ef3a04091dddba2739185fdf6 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:36:27 +0100 Subject: [PATCH 071/162] fix: add DocFX to PATH using environment files in GitHub actions workflow --- .github/workflows/generate-docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index e79e810..dd1d980 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -22,7 +22,8 @@ jobs: run: dotnet tool install -g docfx - name: Add DocFX to PATH - run: echo "::add-path::$HOME/.dotnet/tools" + run: | + echo "##[add-path]$HOME/.dotnet/tools" echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Navigate to project directory working-directory: src/WebExpress.WebCore From 46d7f73c1f24c8c51b71b0ffccc685fe918b555b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:37:00 +0100 Subject: [PATCH 072/162] fix: add DocFX to PATH using environment files in GitHub Actions workflow --- .github/workflows/generate-docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index dd1d980..c34c696 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -23,7 +23,8 @@ jobs: - name: Add DocFX to PATH run: | - echo "##[add-path]$HOME/.dotnet/tools" echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + echo "##[add-path]$HOME/.dotnet/tools" + echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Navigate to project directory working-directory: src/WebExpress.WebCore From de346ec4d22921fd0279681150962facdaa1cc79 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:38:49 +0100 Subject: [PATCH 073/162] fix: add DocFX to PATH using environment files in GitHub actions workflow --- .github/workflows/generate-docs.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index c34c696..d07f08f 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -22,9 +22,7 @@ jobs: run: dotnet tool install -g docfx - name: Add DocFX to PATH - run: | - echo "##[add-path]$HOME/.dotnet/tools" - echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Navigate to project directory working-directory: src/WebExpress.WebCore From 7691588ea28f5bb6f8cb8743e51fecd711a6bf79 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:44:02 +0100 Subject: [PATCH 074/162] add: docfx.json to src/WebExpress.WebCore directory --- src/WebExpress.WebCore/docfx.json | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/WebExpress.WebCore/docfx.json diff --git a/src/WebExpress.WebCore/docfx.json b/src/WebExpress.WebCore/docfx.json new file mode 100644 index 0000000..14eda16 --- /dev/null +++ b/src/WebExpress.WebCore/docfx.json @@ -0,0 +1,38 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ "**/*.csproj" ], + "cwd": "./src", + "exclude": [ "**/bin/**", "**/obj/**" ] + } + ], + "dest": "api" + } + ], + "build": { + "content": [ + { + "files": [ "**/*.md", "api/**/*.yml" ], + "cwd": "./src", + "exclude": [ "**/bin/**", "**/obj/**" ] + } + ], + "resource": [ + { + "files": [ "**/*.png", "**/*.jpg" ], + "cwd": "./src", + "exclude": [ "**/bin/**", "**/obj/**" ] + } + ], + "dest": "docs", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [], + "postProcessors": [], + "markdownEngineName": "dfm", + "noLangKeyword": false, + "keepFileLink": false + } +} \ No newline at end of file From f7ddf5cc6348114bad3459964466b8e58d10fd24 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:47:57 +0100 Subject: [PATCH 075/162] add: configure git user identity in GitHub Actions workflow --- .github/workflows/generate-docs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index d07f08f..f40ead7 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -28,6 +28,11 @@ jobs: working-directory: src/WebExpress.WebCore run: dotnet restore + - name: Configure Git + run: | + git config --global user.email "rene_schwarzer@hotmail.de" + git config --global user.name "ReneSchwarzer" + - name: Generate documentation working-directory: src/WebExpress.WebCore run: docfx build From a650ac13cb6273b6d1546298337bab441a3356b2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 11:52:08 +0100 Subject: [PATCH 076/162] fix: generate documentation in /docs directory --- .github/workflows/generate-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index f40ead7..ca6b0e2 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -35,7 +35,7 @@ jobs: - name: Generate documentation working-directory: src/WebExpress.WebCore - run: docfx build + run: docfx build -o ../docs - name: Commit and push documentation run: | From 53c869af19462bf273e2c12aacde663eec933160 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 12:02:50 +0100 Subject: [PATCH 077/162] fix: add untracked docs directory to GitHub Actions workflow --- .github/workflows/generate-docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index ca6b0e2..e3232c9 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -35,10 +35,10 @@ jobs: - name: Generate documentation working-directory: src/WebExpress.WebCore - run: docfx build -o ../docs + run: docfx build -o ../../docs - name: Commit and push documentation run: | - git add -f docs/ + git add -f docs git commit -m "Update documentation" git push From 9257ae733d6c7081eac3c2e86247aa81a849bed0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 15 Dec 2024 12:45:50 +0100 Subject: [PATCH 078/162] add: Personal Access Token for GitHub Actions to push changes --- .github/workflows/generate-docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index e3232c9..298aa02 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -38,6 +38,8 @@ jobs: run: docfx build -o ../../docs - name: Commit and push documentation + env: + GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} run: | git add -f docs git commit -m "Update documentation" From a6642e8066fba3f52abf1b35dc17d2154387c891 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 18:43:28 +0100 Subject: [PATCH 079/162] fix: configure git push using Personal Access Token (ACTIONS_DOC) --- .github/workflows/generate-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 298aa02..0935e0c 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -43,4 +43,5 @@ jobs: run: | git add -f docs git commit -m "Update documentation" + git remote set-url origin https://ReneSchwarzer:${{ secrets.ACTIONS_DOC }}@github.com/ReneSchwarzer/WebExpress.WebCore.git git push From 6ec481dba68e6bca80acb0a0c38feb545f062d54 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 18:54:52 +0100 Subject: [PATCH 080/162] fix: configure git push using Personal Access Token (ACTIONS_DOC) --- .github/workflows/generate-docs.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 0935e0c..f9e0f2f 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -13,24 +13,24 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Setup .NET SDK - uses: actions/setup-dotnet@v3 - with: + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: dotnet-version: '9.0.x' - - name: Install DocFX - run: dotnet tool install -g docfx - - - name: Add DocFX to PATH + - name: Install DocFX + run: dotnet tool install -g docfx + + - name: Add DocFX to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Navigate to project directory working-directory: src/WebExpress.WebCore run: dotnet restore - - name: Configure Git - run: | - git config --global user.email "rene_schwarzer@hotmail.de" + - name: Configure Git + run: | + git config --global user.email "rene_schwarzer@hotmail.de" git config --global user.name "ReneSchwarzer" - name: Generate documentation @@ -38,10 +38,10 @@ jobs: run: docfx build -o ../../docs - name: Commit and push documentation - env: + env: GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} run: | - git add -f docs + git add -f ../../docs git commit -m "Update documentation" git remote set-url origin https://ReneSchwarzer:${{ secrets.ACTIONS_DOC }}@github.com/ReneSchwarzer/WebExpress.WebCore.git - git push + git push origin HEAD:develop From e72383d3d828389c9947bd9e845687bde0f8f536 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 18:55:52 +0100 Subject: [PATCH 081/162] fix: configure git push using Personal Access Token (ACTIONS_DOC) --- .github/workflows/generate-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index f9e0f2f..d7f6190 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -41,7 +41,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} run: | - git add -f ../../docs + git add -f docs git commit -m "Update documentation" git remote set-url origin https://ReneSchwarzer:${{ secrets.ACTIONS_DOC }}@github.com/ReneSchwarzer/WebExpress.WebCore.git git push origin HEAD:develop From 1d9be164de43e9a0678f2d0ef955ceb36aa9996d Mon Sep 17 00:00:00 2001 From: ReneSchwarzer Date: Mon, 16 Dec 2024 18:12:10 +0000 Subject: [PATCH 082/162] Update documentation --- docs/docs/manifest.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 docs/docs/manifest.json diff --git a/docs/docs/manifest.json b/docs/docs/manifest.json new file mode 100644 index 0000000..235e9c5 --- /dev/null +++ b/docs/docs/manifest.json @@ -0,0 +1,4 @@ +{ + "source_base_path": "/home/runner/work/WebExpress.WebCore/WebExpress.WebCore/src/WebExpress.WebCore", + "files": [] +} \ No newline at end of file From 8ee6b3d36e1c4a7d61253935e9097cbb5b6bb95f Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 19:25:09 +0100 Subject: [PATCH 083/162] fix: generate and commit documentation to correct directory --- .github/workflows/generate-docs.yml | 14 +++++++------- src/WebExpress.WebCore/docfx.json => docfx.json | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) rename src/WebExpress.WebCore/docfx.json => docfx.json (87%) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index d7f6190..ba5425d 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -28,20 +28,20 @@ jobs: working-directory: src/WebExpress.WebCore run: dotnet restore + - name: Generate documentation + run: | + docfx build + - name: Configure Git run: | git config --global user.email "rene_schwarzer@hotmail.de" git config --global user.name "ReneSchwarzer" - - name: Generate documentation - working-directory: src/WebExpress.WebCore - run: docfx build -o ../../docs - - name: Commit and push documentation env: GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} run: | - git add -f docs + cd ../docs + git add -f . git commit -m "Update documentation" - git remote set-url origin https://ReneSchwarzer:${{ secrets.ACTIONS_DOC }}@github.com/ReneSchwarzer/WebExpress.WebCore.git - git push origin HEAD:develop + git push origin HEAD:develop \ No newline at end of file diff --git a/src/WebExpress.WebCore/docfx.json b/docfx.json similarity index 87% rename from src/WebExpress.WebCore/docfx.json rename to docfx.json index 14eda16..b62b760 100644 --- a/src/WebExpress.WebCore/docfx.json +++ b/docfx.json @@ -4,7 +4,7 @@ "src": [ { "files": [ "**/*.csproj" ], - "cwd": "./src", + "cwd": "src", "exclude": [ "**/bin/**", "**/obj/**" ] } ], @@ -15,18 +15,18 @@ "content": [ { "files": [ "**/*.md", "api/**/*.yml" ], - "cwd": "./src", + "cwd": "src", "exclude": [ "**/bin/**", "**/obj/**" ] } ], "resource": [ { "files": [ "**/*.png", "**/*.jpg" ], - "cwd": "./src", + "cwd": "src", "exclude": [ "**/bin/**", "**/obj/**" ] } ], - "dest": "docs", + "dest": "../docs", "globalMetadataFiles": [], "fileMetadataFiles": [], "template": [], From 00d12f878085195434636b1cebe13105b81ee0c6 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 19:27:52 +0100 Subject: [PATCH 084/162] fix: generate and commit documentation to correct directory --- .github/workflows/generate-docs.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index ba5425d..d352a6e 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -32,16 +32,10 @@ jobs: run: | docfx build - - name: Configure Git - run: | - git config --global user.email "rene_schwarzer@hotmail.de" - git config --global user.name "ReneSchwarzer" - - name: Commit and push documentation env: GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} run: | - cd ../docs - git add -f . + git add -f docs git commit -m "Update documentation" git push origin HEAD:develop \ No newline at end of file From 7dc01b9e5acba33e7ee0701e00a05f007c6825f9 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 19:29:45 +0100 Subject: [PATCH 085/162] add identity --- .github/workflows/generate-docs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index d352a6e..08225c0 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -32,6 +32,11 @@ jobs: run: | docfx build + - name: Configure Git + run: | + git config --global user.email "rene_schwarzer@hotmail.de" + git config --global user.name "ReneSchwarzer" + - name: Commit and push documentation env: GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} From cf410ef63415a774aa1ae209f1a9e9beac40384d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 16 Dec 2024 21:13:14 +0100 Subject: [PATCH 086/162] feat: configure GitHub Actions for documentation generation and deployment --- .github/workflows/generate-docs.yml | 49 ++++++++++--------- docfx.json | 16 +++--- docs/docs/manifest.json | 4 -- .../Data/MockIdentity.cs | 2 - 4 files changed, 36 insertions(+), 35 deletions(-) delete mode 100644 docs/docs/manifest.json diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 08225c0..3d28d06 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -1,22 +1,34 @@ -name: Generate Documentation +name: Generate and Deploy Documentation on: push: branches: - develop +permissions: + actions: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + jobs: - build: + publish-docs: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup .NET SDK uses: actions/setup-dotnet@v3 with: - dotnet-version: '9.0.x' + dotnet-version: 9.x - name: Install DocFX run: dotnet tool install -g docfx @@ -24,23 +36,14 @@ jobs: - name: Add DocFX to PATH run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - - name: Navigate to project directory - working-directory: src/WebExpress.WebCore - run: dotnet restore - - name: Generate documentation - run: | - docfx build - - - name: Configure Git - run: | - git config --global user.email "rene_schwarzer@hotmail.de" - git config --global user.name "ReneSchwarzer" - - - name: Commit and push documentation - env: - GITHUB_TOKEN: ${{ secrets.ACTIONS_DOC }} - run: | - git add -f docs - git commit -m "Update documentation" - git push origin HEAD:develop \ No newline at end of file + run: docfx docfx.json + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'docs' + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docfx.json b/docfx.json index b62b760..36759db 100644 --- a/docfx.json +++ b/docfx.json @@ -3,8 +3,8 @@ { "src": [ { - "files": [ "**/*.csproj" ], - "cwd": "src", + "files": [ "**/WebExpress.WebCore.csproj" ], + "cwd": "src/WebExpress.WebCore", "exclude": [ "**/bin/**", "**/obj/**" ] } ], @@ -15,18 +15,22 @@ "content": [ { "files": [ "**/*.md", "api/**/*.yml" ], - "cwd": "src", + "cwd": "src/WebExpress.WebCore", "exclude": [ "**/bin/**", "**/obj/**" ] + }, + { + "files": [ "README.md" ], + "cwd": "." } ], "resource": [ { "files": [ "**/*.png", "**/*.jpg" ], - "cwd": "src", + "cwd": "src/WebExpress.WebCore", "exclude": [ "**/bin/**", "**/obj/**" ] } ], - "dest": "../docs", + "dest": "docs", "globalMetadataFiles": [], "fileMetadataFiles": [], "template": [], @@ -35,4 +39,4 @@ "noLangKeyword": false, "keepFileLink": false } -} \ No newline at end of file +} diff --git a/docs/docs/manifest.json b/docs/docs/manifest.json deleted file mode 100644 index 235e9c5..0000000 --- a/docs/docs/manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "source_base_path": "/home/runner/work/WebExpress.WebCore/WebExpress.WebCore/src/WebExpress.WebCore", - "files": [] -} \ No newline at end of file diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentity.cs b/src/WebExpress.WebCore.Test/Data/MockIdentity.cs index 175466b..decee36 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentity.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentity.cs @@ -57,7 +57,5 @@ public void Assign(IEnumerable groups) { _groups.AddRange(groups); } - - } } From bf6cbdf49ecf08bb313b0ea8ec876c75bcae25e5 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 17 Dec 2024 22:32:40 +0100 Subject: [PATCH 087/162] feat: configure DocFX --- .github/workflows/generate-docs.yml | 2 +- README.md | 5 +- docfx.json | 42 ---------------- docs/.gitkeep | 0 docs/api/toc.yml | 2 + docs/docfx.json | 56 +++++++++++++++++++++ docs/index.md | 11 ++++ docs/template/dashboard.html.tmpl | 47 +++++++++++++++++ docs/template/public/main.css | 10 ++++ docs/template/public/main.js | 14 ++++++ docs/template/schemas/Dashboard.schema.json | 45 +++++++++++++++++ docs/toc.yml | 8 +++ 12 files changed, 198 insertions(+), 44 deletions(-) delete mode 100644 docfx.json delete mode 100644 docs/.gitkeep create mode 100644 docs/api/toc.yml create mode 100644 docs/docfx.json create mode 100644 docs/index.md create mode 100644 docs/template/dashboard.html.tmpl create mode 100644 docs/template/public/main.css create mode 100644 docs/template/public/main.js create mode 100644 docs/template/schemas/Dashboard.schema.json create mode 100644 docs/toc.yml diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 3d28d06..2634fa3 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -42,7 +42,7 @@ jobs: - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: 'docs' + path: '_site' - name: Deploy to GitHub Pages id: deployment diff --git a/README.md b/README.md index c16c145..cb12d87 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# WebExpress `WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net language (e.g. C#). Some advantages of `WebExpress` are: @@ -28,7 +31,7 @@ If you're looking to get started with `WebExpress`, we would recommend using the - [development guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) # Learning -The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you’re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. +The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. - [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) - [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) diff --git a/docfx.json b/docfx.json deleted file mode 100644 index 36759db..0000000 --- a/docfx.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "metadata": [ - { - "src": [ - { - "files": [ "**/WebExpress.WebCore.csproj" ], - "cwd": "src/WebExpress.WebCore", - "exclude": [ "**/bin/**", "**/obj/**" ] - } - ], - "dest": "api" - } - ], - "build": { - "content": [ - { - "files": [ "**/*.md", "api/**/*.yml" ], - "cwd": "src/WebExpress.WebCore", - "exclude": [ "**/bin/**", "**/obj/**" ] - }, - { - "files": [ "README.md" ], - "cwd": "." - } - ], - "resource": [ - { - "files": [ "**/*.png", "**/*.jpg" ], - "cwd": "src/WebExpress.WebCore", - "exclude": [ "**/bin/**", "**/obj/**" ] - } - ], - "dest": "docs", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "template": [], - "postProcessors": [], - "markdownEngineName": "dfm", - "noLangKeyword": false, - "keepFileLink": false - } -} diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/docs/api/toc.yml b/docs/api/toc.yml new file mode 100644 index 0000000..20c32af --- /dev/null +++ b/docs/api/toc.yml @@ -0,0 +1,2 @@ +### YamlMime:TableOfContent +[] diff --git a/docs/docfx.json b/docs/docfx.json new file mode 100644 index 0000000..b6232e8 --- /dev/null +++ b/docs/docfx.json @@ -0,0 +1,56 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json", + "metadata": [ + { + "src": [ + { + "files": [ + "src/WebExpress.WebCore/*.csproj" + ], + "src": "./" + } + ], + "dest": "api", + "outputFormat": "apiPage" + } + ], + "build": { + "content": [ + { + "files": [ "**/*.{md,yml}" ], + "exclude": [ "_site/**", "obj/**" ] + } + ], + "resource": [ + { + "files": [ "**/images/**", "**/media/**", "codesnippet/**" ], + "exclude": [ "_site/**", "obj/**" ] + }, + { + "src": "../schemas", + "files": [ "**/*.json" ], + "dest": "schemas" + } + ], + "postProcessors": [ "ExtractSearchIndex" ], + "globalMetadata": { + "_appTitle": "WebExpress.WebCore", + "_appName": "WebExpress.WebCore", + "pdf": false + }, + "markdownEngineProperties": { + "alerts": { + "TODO": "alert alert-secondary" + } + }, + "xref": [ + "../.xrefmap.json" + ], + "output": "../_site", + "template": [ + "default", + "modern", + "template" + ] + } +} diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..f9859f8 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,11 @@ +--- +_layout: landing +--- + +# This is the **HOMEPAGE**. + +Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. + +## Quick Start Notes: + +1. Add images to the *images* folder if the file is referencing an image. \ No newline at end of file diff --git a/docs/template/dashboard.html.tmpl b/docs/template/dashboard.html.tmpl new file mode 100644 index 0000000..0288fe8 --- /dev/null +++ b/docs/template/dashboard.html.tmpl @@ -0,0 +1,47 @@ +{{!Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license.}} +{{!master(layout/_master.tmpl)}} +

{{title}}

+{{#items.Length}} +
+{{#items}} +
+ {{name}} +
+
{{name}}
+

{{{description}}}

+
+
+ {{#usage}} +
+ {{#config}}
docfx.json: {{config}}
{{/config}} + {{#command}}
docfx: {{command}}
{{/command}} + {{#init}}
docfx init: {{init}}
{{/init}} +
+ {{/usage}} +
+
+{{/items}} +
+{{/items.Length}} + + diff --git a/docs/template/public/main.css b/docs/template/public/main.css new file mode 100644 index 0000000..b0b8c1f --- /dev/null +++ b/docs/template/public/main.css @@ -0,0 +1,10 @@ +/** + * Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + */ + +/* Checkout https://getbootstrap.com/docs/5.3/customize/color/ for more customization options */ +body { + --bs-link-color-rgb: 66, 184, 131 !important; + --bs-link-hover-color-rgb: 64, 180, 128 !important; +} diff --git a/docs/template/public/main.js b/docs/template/public/main.js new file mode 100644 index 0000000..d5c4867 --- /dev/null +++ b/docs/template/public/main.js @@ -0,0 +1,14 @@ +/** + * Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + */ + +export default { + iconLinks: [ + { + icon: 'github', + href: 'https://github.com/dotnet/docfx', + title: 'GitHub' + } + ] +} diff --git a/docs/template/schemas/Dashboard.schema.json b/docs/template/schemas/Dashboard.schema.json new file mode 100644 index 0000000..a00ab75 --- /dev/null +++ b/docs/template/schemas/Dashboard.schema.json @@ -0,0 +1,45 @@ +{ + "title": "Dashboard", + "$schema": "https://dotnet.github.io/docfx/schemas/v1.0/schema.json#", + "version": "1.0.0", + "description": "Schema for dashboard", + "id": "https://github.com/dotnet/docfx/schemas/Dashboard.schema.json", + "type": "object", + "properties": { + "uid": { + "type": "string", + "contentType": "uid" + }, + "title": { + "type": "string", + "tags": [ + "localizable" + ] + }, + "description": { + "type": "string", + "contentType": "markdown", + "tags": [ + "localizable" + ] + }, + "items": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string", + "contentType": "markdown", + "tags": [ + "localizable" + ] + } + }, + "type": "object" + }, + "type": "array" + } + } +} \ No newline at end of file diff --git a/docs/toc.yml b/docs/toc.yml new file mode 100644 index 0000000..260d481 --- /dev/null +++ b/docs/toc.yml @@ -0,0 +1,8 @@ +- name: Home + href: index.md +- name: API Documentation + href: api/index.md +- name: User Guide + href: user-guide.md +- name: FAQ + href: faq.md \ No newline at end of file From 2d4351446a668a6a572f149b72a08e6073561643 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 17 Dec 2024 22:35:34 +0100 Subject: [PATCH 088/162] fix: ensure correct configuration for DocFX and GitHub Actions workflow --- .github/workflows/generate-docs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index 2634fa3..e347b7f 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -37,7 +37,9 @@ jobs: run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Generate documentation - run: docfx docfx.json + run: | + cd docs + docfx build docfx.json - name: Upload artifact uses: actions/upload-pages-artifact@v3 From 8f34ed4f3e2c5f02f740c689d19c268061ee1993 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 17 Dec 2024 22:43:02 +0100 Subject: [PATCH 089/162] add: link to api pages --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index cb12d87..3b20fcc 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The current binaries are available for download [here](https://github.com/ReneSc # Start If you're looking to get started with `WebExpress`, we would recommend using the following documentation. It can help you understand the platform. +- [api](https://reneschwarzer.github.io/WebExpress.WebCore/) - [installation guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) - [development guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) From 61777b852e084d89e39cbcc324f03a298d45de58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:38:04 +0100 Subject: [PATCH 090/162] Update index.md --- docs/index.md | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/index.md b/docs/index.md index f9859f8..5acb2cc 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,11 +1,42 @@ ---- -_layout: landing ---- +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) -# This is the **HOMEPAGE**. +# WebExpress -Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. +WebExpress is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing +a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .NET +language (e.g. C#). Some advantages of WebExpress are: -## Quick Start Notes: +- It is easy to use. +- It offers a variety of features and tools that can help you build and manage your website. +- It is fast and efficient and can help you save time and money. +- It is flexible and can be customized to meet your specific requirements. -1. Add images to the *images* folder if the file is referencing an image. \ No newline at end of file +The WebExpress family includes the following projects: + +- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for WebExpress applications and the documentation. +- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for WebExpress applications. +- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for WebExpress applications. +- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for WebExpress applications. +- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for WebExpress applications. + +# WebExpress.WebCore + +WebCore is part of the WebExpress family and includes the basic elements of a WebExpress application. + +# Download + +The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). + +# Start + +To get started with WebExpress, use the following links and tutorials: + +- [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) +- [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) + +## Tutorials + +- [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) + +# Tags +#Raspberry #Raspbian #IoT #NETCore #WebExpress From 41e4abaa54ec2ffdea7deeb9f8482ea40c6e9491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:41:16 +0100 Subject: [PATCH 091/162] update docfx.json --- docs/docfx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docfx.json b/docs/docfx.json index b6232e8..cc27a86 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -7,7 +7,7 @@ "files": [ "src/WebExpress.WebCore/*.csproj" ], - "src": "./" + "src": "../" } ], "dest": "api", From b261f2f2def5670bf7f81ba22b27e016f45f18b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:19:17 +0100 Subject: [PATCH 092/162] update generate-docs.yml --- .github/workflows/generate-docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate-docs.yml b/.github/workflows/generate-docs.yml index e347b7f..23b2392 100644 --- a/.github/workflows/generate-docs.yml +++ b/.github/workflows/generate-docs.yml @@ -39,7 +39,8 @@ jobs: - name: Generate documentation run: | cd docs - docfx build docfx.json + docfx metadata + docfx build - name: Upload artifact uses: actions/upload-pages-artifact@v3 From 13235e814bd28ee0a73d1701dd282cd6d35c16bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:34:15 +0100 Subject: [PATCH 093/162] update toc.yml --- docs/toc.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/toc.yml b/docs/toc.yml index 260d481..1b4ceba 100644 --- a/docs/toc.yml +++ b/docs/toc.yml @@ -1,8 +1,8 @@ - name: Home href: index.md - name: API Documentation - href: api/index.md + href: api/WebExpress.WebCore.html - name: User Guide href: user-guide.md -- name: FAQ - href: faq.md \ No newline at end of file +- name: Tutorials + href: tutorials.md From 4aaaabe9af2f2342d7fc3cffab53557726427235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:39:28 +0100 Subject: [PATCH 094/162] add tutorials.md --- docs/tutorials.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/tutorials.md diff --git a/docs/tutorials.md b/docs/tutorials.md new file mode 100644 index 0000000..182d1a8 --- /dev/null +++ b/docs/tutorials.md @@ -0,0 +1,15 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# Tutorials +Welcome to the `WebExpress` Tutorials! Here, you'll find step-by-step guides and helpful resources to get the most out +of `WebExpress`. Whether you're a beginner just starting out or an experienced developer looking to expand your skills, +our tutorials offer something for everyone. + +# Getting Started +Begin with our basic tutorial: +- [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) + +This tutorial will guide you through the initial steps of creating and running your first `WebExpress` application. + +Stay tuned for more exciting and educational tutorials to help you unlock the full potential of `WebExpress`. Happy coding and +best of luck with your projects! From e07befcb146c4a5b54b3b9d49f1d5b871e9c6c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:44:18 +0100 Subject: [PATCH 095/162] add user-guide.md --- docs/user-guide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 docs/user-guide.md diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 0000000..8aaffdc --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,13 @@ +![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) + +# User guide +Welcome to the `WebExpress.WebCore` User Guide. This guide will help you get started with `WebExpress.WebCore` and make the most out of its +features. Follow the links below to begin your journey. + +# Getting started +To get started with WebExpress.WebCore, use the following guides: + +- [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) +- [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) + +We hope you enjoy using `WebExpress.WebCore` and find it valuable for your projects. Happy coding! From 77e7c591a6a2d1b9bf3d096f6c65b1ba2184d65d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:45:51 +0100 Subject: [PATCH 096/162] update index.md --- docs/index.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/index.md b/docs/index.md index 5acb2cc..3824b18 100644 --- a/docs/index.md +++ b/docs/index.md @@ -24,19 +24,7 @@ The WebExpress family includes the following projects: WebCore is part of the WebExpress family and includes the basic elements of a WebExpress application. # Download - The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). -# Start - -To get started with WebExpress, use the following links and tutorials: - -- [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) -- [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) - -## Tutorials - -- [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) - # Tags #Raspberry #Raspbian #IoT #NETCore #WebExpress From 9fbc5d6af4d336f3e121a2d38498c4ee2b4e4951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:07:57 +0100 Subject: [PATCH 097/162] create webexpress.svg --- docs/assets/webexpress.svg | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/assets/webexpress.svg diff --git a/docs/assets/webexpress.svg b/docs/assets/webexpress.svg new file mode 100644 index 0000000..d49a8c8 --- /dev/null +++ b/docs/assets/webexpress.svg @@ -0,0 +1,40 @@ + + + + + + + + + + template + + + Tabelle.2 + + + + Tabelle.35 + + + + From b6465c134646848beb3102ef13d9b3d7a62bb6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:10:44 +0100 Subject: [PATCH 098/162] add webexpress.ico --- docs/assets/webexpress.ico | Bin 0 -> 70141 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/webexpress.ico diff --git a/docs/assets/webexpress.ico b/docs/assets/webexpress.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0909c98b13c6125b7254544eaa2a1d91238b006 GIT binary patch literal 70141 zcmeI52|QIz|G>vC;Zl(bA*G}!m84Kw5iPQ2i9#w;l8_}k*(ys#)+EZlMA_FQTgbjg zS;|hf`=4_?-S^SegLf*y6=Qzqqx;%6sY8G83sf z2$368aFl1QWaU_yZF^9Z`~UN_ta+o;BKWt9NZB zv3OzQT25k>a%US4AMv^a8g#A89rp{fQ}zv7ozR|e2rED2SW40wbyJO5z1XJt`}NmV zpQ%CTW~&r2zAfBjt>oG4n>JFFrUuXnIc(`q^z$85V%}0HDtiO6Rd(mekn6P64~X_= z*+>2%@E-lw<Kh=if`&N ztI4t0{Y;>3W3F|!+NDl$Ny@baxoW^Ii1OM%X2_lM@14M?c0U}U-f(0?b&oLj4sKR$7@PR;oU;~@2%bge{+#f*sfrTXx%an`-2>>8ym-57+DR-0g*+blaR1t6zg-VBfATQ zy>`kBr1Jx9dp+}R)hO0u>{iS=X`kxFSxw0;G$g3))8DRteB9VewX*VVtiW}*llm+b zEXQBTDw97AS#!V=d{tjbP2+MfPMY%Goj^W84*IP{_k^oQl#a)D18R`BbMIBUGEdz* zk&mC1)}9Kl*sbVo>08ZpHH2p+OPDJ5Mv9Ew!~k~u^>CKY=jhSf1(J7Zo#H=Fc_mgx zF9&pLVqu=|o7B=Krs#L4bYB=vUL!CXuee1x$(UBDOeE*#&YYA6mlJH+alM?EeN&0E z?r^+M#d3R45k>MGj18=B9w(L~GvwQ3d2++5iHesr-bv#MJbMQ3owQmDb|})6iKk`R zn+w~fJ!zovsCnc1IjoI_c>QT_q*DV=E1> zg*jGlVgfGgnVTu|SgW)LdChN~-qLP1L9FZ_dx8p&ahrrX!>izZ%1Gi;Twin$x$e=0GQ-@v?0n$VJ!j5}9R=fbIv0*(|EU zy&Se=Yq0V?li$iEa!A3?hgr2(Ds(p@JmUZ%VgBAm3eTaI;8xf`y5iRkUZ8t{t7%o#G zuHteRwIBIvR>leIi?+=?=ws7LDr=)_svO&q*`Wnj{0M@qpy;eOW3e(|fN$R*d)4h?3> zNvIxB7qu>mDbQsYZhx}N*t|^dQ*00)=)T^Yw(B;7?A<3*W`30N=honM~G7+H;Z|^vGlbeQd4Ejski+2P4QBH zkIM71$)UQ7^lzC~uQ)f@IM!40madDE-dWblr?UZj$M2QC15V@K#Jb*MLFtTR=f}8` zlZ>eoY{b6ty=|l#8q5ohI=AN?!_NAzLyfyt9=xR;P#t9zT}#>5YZIHlL|1*~?n^c$ zI&l`@>a}YJQu^(LzP>D=-O*`uaMunt=8F=$9`|VY+%SIeSisQYOM73QkzJF4f|Tn8 zDxCt8_?~t{!AR^Xz^HpRfU;FgW7{TECUsK^wewgeBZ<1Ej;~J##2zVgGw9T;4xk6s ziF%@y)s9Yy{qAz79k<_N@vmqZC0X-6Su;&Ec$~EK(B))?+sh7+6IJi?I;mqKaAq|8 zarM5fGVqu5!$k7y?`meu;cr!(eN7p@nT%8}bk#9_|=aqqEhs2Qx^(BW_8zr#I88)52xJ!ta#DC;$ z&a%DxbXX%ql9x*#f3>nqSn*v$*#_&JtTTDaX7H|QG*4ymRZdCEn!57k%tUK}zVD|H zYpy`Q@Ynlmg-!Pa{*`RVn%2g#jZu$G01e>uKIDk}3pzKF_ilgf#crwqjE4NteDjqxX_k zT&g0xj_YHwMyciTP=O}pNvBUA`G!-tN`fzcP}o&fPchKCfk|fMg(Go_F3+Cr4Wtuf z8VA&)Zf5K`IA*)sRH@MEv4v3@Ut3qSOM@2KmsbX_DQlmJ+<90U)YRVH@AN55admHJ zj+N1tE`Ecqq=rVnyhgp=HLkT=nM=9~Gu`zM!N(!XGRnTZZA+u{JGbu^a;Rn#8Jket z)6P<*U7|*1FpxVK+I*DyuJ!AKR30H~UOF23Y<`!O$nc#!#bae#yG@ttS8FdBY!|jb zbCr67v9}(!k6KhjWtl+tqj9b}79mgPKGm-dYwT?F??;JXeHySK2`Nin_or?@wlQsA zm%bIb2zX;Ur9o@fKvGhy=11qFFt(k-9Mj+(Pp)r9!ZUSKZo~U@pV2<@$;Sold%6pc zx9-;fpBcN`t@z(1un8LQnTTOb>-Xw6rFJy8swZ)=52)uhJ6GFAUXL2eBr-6(+2_9v z431yu`u3&xiND>ebj#gIqk6sfA7SG=Ojo zNvPM-SrsxwcE!7C=|z4=PU6T&gx%}d<}T*TW11q7ldHhkz||WXynH8urTMn*eZwN{ z@@Q#5x`Fb>l8U8%Jo2tL+l4E_carnUF}9E_uQNIicDoHr6)o$c5pY;htM&5mSiju1 zeH=NVY*!ft&)q2CIcq++kB4c`bGh0R;T7^G6UT)|1l+Pj$oI-VaIkSmJ1715X(@?@ zzIwH0+Zivl#){agn?pR@Z9~SsovvMLoyJ?a&go~a2zqup?csO9m@p?JQR=o{UgATy zDAr8|^k^z8zH`@(SA4KTmOVTMe0qOlO6x8_mecGs)D{6Lb4c)}p=eSY!{7V|M3t~Gav)H3O+;#qI(T^?n;{T4r?c>-C1 z%8UJ?)E2B_Y#v@Dvd3SZ)Og#xWutDGx&|L8yeXK@Pt+CH(rXy((VOf?q-M!)xu;=y z6n8BzbGDtFbS%51wJXt$qtgws_hU~)?E;q%Q3*!KdWeQ-AJVX6zTjN(Sth(>O9^pF zV6?JJt@91ap*}l1r!{YUf{#+KvUU$BJxb}gV?6KZiwvPvN1`|MtTnB5B~p9M&UWe2 zVcqw~+sYrXrKK{jEEv##{W2qT3D17r26-lP3QtbSoZhKY!&}r1@AkMHNo@;HrM3{h z*Rt?{(FK=%*cP-@sS8T3g6P8Z$r%f)ydaFKx-eB$W7_T zx&t@)UC-^L5Ftr7E+Wm;*ZCrWr6*qPO#UUYzwcbpCgx3r7B6CRxI1eavoI9j%vpoT zsiWT}G`RN(vL3I^$S4L;MRX?&!=v~+NQHR|MGcQToT;oh-hHQjz%S!XRo||PT%kyx z^a71YeOQSv?x%%wJz8<)b7knu)QBREZL|$oX3f02(GCgqM!LKzqsADDwDnt8R+FZz z>(Y*P$iM()9R^S4nmayW>fsU34hLmCT8GOizx}M1gjk;*OgM&FVQj zeIoOZ+)xT?Fb|4~US=*ByR=p_A|~wiH<=sf-uHd%%QzF!k!J#i%?j4jan$kCl?t%(zABu`-%hoQ}6iNz*eZ3h{I;2$&(b_%D0|l$+GC!oT-GPoSEYT zSbM|xWfV)d4$3EpJp07V!|kB<mwFC9dkJ77t7ntVgkTiA=^ zH-1ndnMc#v;Usc5Wo5DNq<`Kug$kP!2Ai5&c%tR&8PcDx$m2I$4^|S+j;(O#rqu9Cq~bTc!zduuF(%ar0G5uTA?%XCHu9}L|ZNM#9opp zWwA+$milUGweEDI@CdC?FOk4Ecdm-%J-StWMu4uTv9WvT`wr~hgbU2)Dbzt#M79N` zk3s^OulDmN>| zitclJj!QO(I)2QOo{5F=l9C7G=dJ3`ZxN}xFE?G!+I^H7)IOkFanZ!1vbCdLtVJhD zBGhf8AtyOd5cdkH4eR8u60)X%&Cth1Ubivv&V`fMV|L;Jc^NC^UkKBuQUh`eXYPv; zxt0?HhKZ_0jC&`Fx^<|5Qo``)n*1c?%lt!I+`l|wV+{Z*}nDZ=KyrTv#h6^uu+oX7-fppNrI6bf88t*QfLPM8>uh-& z8jo%^YB27_yE!la=Gv~O-`=BB?DXyHX(lL9dpU|5HH16nYJK_`hlea@Efq4f;QI37_^O^q?Bw;GLZl?=+rBN4{NA)G zR)j+)ykazP^j^`m`xG1G-_UH|ywtx`r)m}Ku|D6=p69FiMBFrj2NNSz&s$r!MxeB1 z+&qrY%GZJ@-GsQ^NBj-1^oX40D6V--dfy!56hTk62F)gnKhOiV{B}^ouqZEWEB`(J3=bC z*JISfpuVww=?HatdH#8)uUe;{G21zo-Aac766}1~<*m<_b<}Om&8bcHi{_OOkN-@} z2ClXl@2MG*f8xUgXiWs&;xytN!Eki=}0Y>=KY%Bgfy&m zkvF^Qoai>{y~ub**4W7-u#JcHq*(xCePkBdm;8uL%|x2OZR?QlOXMcF7EZHb#gw8`>uhjH$K`xpGLlxy!Aw4R3m2{(QLNtOm1a7fiv8KEqq@uEEZe-zhn zr9i51@-wgLpq3bEm0Q>aetBNGXi_3w6_K+eiTRSd!nc-+5OaCy9=5PceA_uT=%uG& z(yp~v`wc1EeP5c;2OBR%98$Rc^4PQT_5yMDWRrcJHIml_uLg9RyvgmDq6RT6u!nTC z{=&pQOP8%=tb)U0*WIp=t$_{53JPun3{Ag%%YLb~_m|s}p*?h#-8#^3a;**~PjktUkM_{e4vrm?Iv8<4 z?aoFRwm>4F6&^AB&$k^m2k0M4040DDKnb7(Py#3clmJQqC4dq@37`Z}0w@8L07?KQ zfD%9npaf6?C;^lJN&qE*5KY=4^4w42z8q~E9VrO9hp}ZOQ?v3=Ob`zt~(#kv#>6iQ1MaH4SdqV zfr-g+d8gHcx@dSd0r5LzZ>!@MAPF&^by>;#3u+P;1~{Es-jqYA_$%H5H*Q z8lENq;m>aE%tv)%V&atDGCVrtz@zg?&W^w8vj?w;27bUNTjZUO=V=orkEbVNzXCQd z@kt8@>|dP4E205N06uURf9K=bzQ}PVq}TDa5udbhz$+0oO8;x3A^5<3TeDDWXFix8 znlcIb=U2zgCpjbDH~AK!;_n=WoveT3&Y$M_ZSV^rx|g{wGdRz8i1MH22F(le^>F7y zM;H%(@ChxV-ii8zsSAWX6LbkH?zauUE)HCP(qfX%y0GEtS%u0jMg__^MyTSoe%pYG zEBoSGEc1cA@L)3@pvAKj^NG!fHx4|4*ZOS(YVMpfu>2c`7rprK0L`9CE;0vY7k0eX z?;Lnnle9=;^NDGBZp8y!R#y;cPp&7w9*{HL(Z87u+Ws-{g2(yaHzfYLDK)9=~P1ECFUmU_re%tU- zOVV3`M-mHtz72LRfDWJ3^ts}U0|Oq{Q48YE-d23-PXq8v2aCh2qX9etXob7{a93ce zMSfLWPk+bkHWD03&;1xJ>&S>#fq_N-_!HI(^b4|nE=QON8lFwyq%G#c1MZvt2X8-> zgpf^xB^B>7&k%$5su zF~?sobR8KQ?awR^HwhAl<_LWJA_Ee4D3Xi)XH)&FF)yHl=G8Gbu`gWaj6 zA<#Il;>NL7 z8t=j<|Nj~O^tTMQ2hQEwu<%vzJVaA8BUx#{j&d}7a@3VI?(A;7) zXIDY=TaU%){)1#Bt(W{ATfuC?3&;E=KdWaMGd&pL147A}d+eOD~dqUx(s@As^ExFKlOUdO=qcHG?=<_TOu83T>MPAfDOYHo8?(*iGzYlqV z9Ip8a6Ebm+(Vo%tfPUnj+i*SQ2zY2X~rf0M-Jf# z7t9~e>bkG+kMGR|%x+>6t}=kvc8fNesLc-{7%`u_-ad?v7kPspmE zCJ~=BBM_W+hfs3%t4yVh8H57=lVNDXH}pT4T;%`L!AICinIM>3UXI)lW&{G}l-$Cs+J zo9l#NQ%XY!sTS7G>u3~~?L;Wq__>@gpXu<7Uo%4a-!>eWYCanjKBnVq10flsfJ1ZE z7iz3lmX#L~(_XHbIQ0yxkN9lhGRYoYI!Hi7TxbZ+$>rSs)> z|9`p_Is??=58;sgd~c1xyWQ;R@x^BazaaC$hw-rmUW?D_w~~~$p)PI6m=hiBUR{2; zsF{~$cmmAMt*r4+?>hZfgnxRmQa03(&unNg1{xV4+XZrfT@TNAy*VGdgc*MA|GYpZ zXp)rmyj1`(!r2V2KM^6cNI+unrAz2dSJ9kH$a%gn{56xdp+Pw>Kmb-jLCvfA0K)jL z-nQJ2G12)RF>k#JYm<8iyA}|houqMZ;{v6lNss_+5kZUbjCSU1lK)=J41+2Pd_Dqq zLS+Vqzx{iV7tpeNc51#7(fAnzLNUj*AQ#TlibhYJ%hcp?HvyB4^m|5DQ3 zNVSEFLsRD@0PUsowN6p~a}xU%kvd-6;@&-kFW>zo=7hrN{r~st1vZf2Ko4w!{u?81 z@Ui~)PXNsUPe9F$(=kE?wpU9UQepRY;fFOGr-s&Pye=B}j}w6Ih}xcfCP5N+Bh()z zUiVEl4otI{{_~8#D(krNKVBl5AxZ!xfD%9npaf6?C;^lJN&qE*51d>t^@$!>(n5t;srQmM+ZVOtO1-L z3*>%`0@h)2;OpQQm`50Z3ts@Zlqcx!`vl-}833-t1mfR41aRVZP}flcuKDf*aGFv8 zXOjbM-8H~JFi%-{UT#45(Qa_og%zIDYXGMO0l8H% z0IpmK%tE99oYDsPB;N#|zcqo9hBR05;klwH_> zjQt8&-WmWGzyRe<+2C2GEr`r_2f^ugATL?~Cy4_Ig^xj6SqOk{$O9ek?T`a8fXm4P zn=pA$So;dV>9K&O#})wJ7Y1V!qrf&?5nS}z207P*@SM9ajR(M2RY7E)8;FJ104KEs z@r927TrCUq4RwR(*$x2Cz7F!MVgX#T81xT!131|?vT7v^_W-@eg0LPz_ZZje<$+`2WSt3{5u&01O`Dhs7^~8{PeG9 z;Oj&C1OPuootD=BFYCKHLA52|F;1PwEqc{ppiO6y3qSY5n=zkFzs&M9@udcY*VCz-6}ByfL;vv}tMW zfc^A=!@Sb76D;rGGq3{I(_x$0V)MtS8k?Bn)Ff{MvuS;Ei}}UpO1XA@oxu&mo4{!G zALnx5)?8w9$6Z;=_D6!?)j!nXhyL8cvt!i{&}wO4B-Xif`HJqp)M;t;W{b`hgZsu2 z>fythbBWK4gQd^jmYrJ$BQ?&?6rSVw>}+ji6+xh?wu6@T+#0EfB-T302l_z)5gzMD#Z;AU#LYN*1WYx)uO7c6jx0TQ`T$LQyN35Uo|K z*B~EoV_ryj&a@Q_)0)%&8RwK)Fp`cQx%vOtmML0fhW*(8BMH3N|kdl#8 zP%inIAexR6Knb7(Py)XPfsODo`QWYw_n+H|DWN@Z12FVI03IjZ0Iy2|L2*MWsQL6B z)IyuBYxD&mVMT$nPi9v}fcCHT050)`vlSQA#N+I_>Ci4b{JkG%Z$)f`>dm0X6q^(C*2Hvymb; z&vW ziGS*;1<$h_aCYxjXwyaPrig7_){zn9RmXrQZ;YYMmkioMB|&pnCA5*o!ZZeO`QZ*| zKmUfa4IeQlf;_U|wRYm{+S1StuHwc4@~h(@=R%+e?Z0r{X;4^~gtIXt_VRjYOGj+S zaA824{rND=L)D!VXJen7ngC&0jyPNMCGQatd5ORv3?|rF9?CxrQT(aGRplp8((kH2{ErDB`l-D4Lqzb=V;|(h z*YYRw_wFx8{xkV&OB=zqU$zDdTl$GP{VA$J{hp2#o3H^cJ{_G=2!;K9`@rpa~6RI+y0X1|>rIv&t$!QB`fjIrVwvrB5(%|4`wE zwj!A`vU2hYOXt6f_8(vnJ9y~u5vX)@4>*Rwh)YOfj!Vs3008_ucJ30`&49rO?g2s= z473knL_`;mpHAAii5s&SvEHoX+lIj`!gjNk4a3d>Z7iJYH()SaKg}Ni#+4W*XkB4u zftSL%2zfxiY&ong+{UmNd4My1;Nq);)Gz(0fl(obcByC~d$)=yP* zUIRQ5u7LFN=eT|;;?INlBO(1T#GeM~uOR+Qh#$@c=+}by*$jUlfPFW6*yp(dDq$a_ zq9q6R5jk=F55zwSu0aaiW3_R8N5q#Ysn`!FIkVvUqb|`JI6oewzl-$S5T6*tKj=}C z0g!iE1^scJ;reEXzYWsALi){!p9|uL6aDr+uK$JjIwAchq~ClR`YIv)z&p^-2QJ+V z`&QR+eIBHLiTD$FB7bX`X=>=T~B z`79y+NQ%zPus{3^*AGN|rw~6I%V#HXeW~JxH@JSZJ?!HmK1Si$PPo1*;@gAt*$`hY z#DB;F$|u0SEz<8s`nWA!A7QPa8%h8r@c)g#PUUU$o|1whP@A9|jh&!S=|X`a>ZZws9)199X!tXWSyxL1sl*oMVJ47LkQEXe{*W z#_diKYPh(C@__d7IOs2plP_z^jDx<&czqz!OMFlT*X5BZ`_aa zkx%GPKIb{4e|Y{UT|^cxE{lbWivLMJxj0}>eYlJxzT6+>L7OwSyRTj0$1(DmUlli} z{^aB&LqSbqoNKfu_VUAB*b5JMuqmZaXPrMBqrgJH@;@=P)r=(uxX~-&q$nY~N51E)VFN zKC6sB>j5lWlNbAKpfe6$rvmg%#~!mJ$GXR8<5+a|w#GsGKlZR0(VY3faSB{n(4QZR zTu*I#G48%V@_>H)xO0FE6xw?l;$V9g3;p_$dX78mvKE)$DZ6Db8LJ>|LyJ2`(w}A8 z;li;6-gDLmBH!HkF;2#T0VfB?7qI2cIdPGB_ps3BZw~#`aq;#q&d#a-;~t0OKye5k zgd0*nFLPb7aO?x8i!eg!`iJa%pU6FL_d*GK#NuD~_7A?QZk#{W|B)|p--PElV=dsl z3fucw=aOw1n!6d4B6pk0rye3Pr)527@H2j{{a?k BFy8 Date: Wed, 18 Dec 2024 13:23:27 +0100 Subject: [PATCH 099/162] update docfx.json - added paths for custom favicon and logo to docfx.json --- docs/docfx.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/docfx.json b/docs/docfx.json index cc27a86..508d9e6 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -26,6 +26,9 @@ "files": [ "**/images/**", "**/media/**", "codesnippet/**" ], "exclude": [ "_site/**", "obj/**" ] }, + { + "files": [ "assets/webexpress.ico", "assets/webexpress.svg" ] + }, { "src": "../schemas", "files": [ "**/*.json" ], @@ -36,6 +39,8 @@ "globalMetadata": { "_appTitle": "WebExpress.WebCore", "_appName": "WebExpress.WebCore", + "_appFaviconPath": "assets/webexpress.ico", + "_appLogoPath": "assets/webexpress.svg" "pdf": false }, "markdownEngineProperties": { From 4b75ef55208acc5c27558cccd73535ab8ed90095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:26:04 +0100 Subject: [PATCH 100/162] update docfx.json - bug fix --- docs/docfx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docfx.json b/docs/docfx.json index 508d9e6..f71e14c 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -40,7 +40,7 @@ "_appTitle": "WebExpress.WebCore", "_appName": "WebExpress.WebCore", "_appFaviconPath": "assets/webexpress.ico", - "_appLogoPath": "assets/webexpress.svg" + "_appLogoPath": "assets/webexpress.svg", "pdf": false }, "markdownEngineProperties": { From 44e4ef83e3542bac807ea6b8b152f99c01f2fd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:32:52 +0100 Subject: [PATCH 101/162] update webexpress.svg fix: resize SVG icon to 51x51 pixels --- docs/assets/webexpress.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/assets/webexpress.svg b/docs/assets/webexpress.svg index d49a8c8..e4547c1 100644 --- a/docs/assets/webexpress.svg +++ b/docs/assets/webexpress.svg @@ -3,7 +3,7 @@ + viewBox="0 0 51 51" xml:space="preserve" color-interpolation-filters="sRGB" class="st3">
UriResource Uri { get; } - - /// - /// Returns the culture. - /// - CultureInfo Culture { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index ff66112..596f380 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; @@ -66,8 +67,8 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon { var pageInstance = CreatePageInstance(endpontContext as IPageContext); var pageType = pageInstance.GetType(); - var pageContetx = endpontContext as IPageContext; - var renderContext = new RenderContext(pageContetx, request); + var pageContext = endpontContext as IPageContext; + var renderContext = new RenderContext(pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); var visualTreeType = pageType.GetInterface(typeof(IPage<>).Name).GetGenericArguments()[0]; @@ -91,7 +92,42 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon del = lambda; } - var visualTreeInstance = Activator.CreateInstance(visualTreeType) as IVisualTree; + // create visual tree instance + var visualTreeInstance = default(IVisualTree); + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = visualTreeType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var parameters = constructor.GetParameters(); + var hubProperties = _componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var contextIdProperty = pageContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.PropertyType == typeof(IComponentId)) + .FirstOrDefault(); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : + parameter.ParameterType == typeof(IPageContext) ? pageContext : + parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(pageContext) : + hubProperties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is IVisualTree visualTree) + { + visualTreeInstance = visualTree; + } + } + } + else + { + visualTreeInstance = Activator.CreateInstance(); + } // execute the cached delegate del.DynamicInvoke(renderContext, visualTreeInstance); @@ -285,7 +321,7 @@ private void Register(IApplicationContext applicationContext) /// Registers pages for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs index f3e2c2f..749661d 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage @@ -19,11 +18,6 @@ public class VisualTreeContext : IVisualTreeContext ///
public UriResource Uri => Request?.Uri; - /// - /// Returns the culture. - /// - public CultureInfo Culture => Request?.Culture; - /// /// Initializes a new instance of the class. /// diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index cef3228..1c31298 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -293,7 +293,8 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon new StatusMessage(message) ); var pageType = pageInstance.GetType(); - var renderContext = new RenderContext(new PageContext(_componentHub.EndpointManager, null, request.Uri, new UriPathSegmentRoot()), request); + var pageContext = new PageContext(_componentHub.EndpointManager, null, request.Uri, new UriPathSegmentRoot()); + var renderContext = new RenderContext(pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); var visualTreeType = pageType.GetInterface(typeof(IStatusPage<>).Name).GetGenericArguments()[0]; @@ -317,7 +318,42 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon del = lambda; } - var visualTreeInstance = Activator.CreateInstance(visualTreeType) as IVisualTree; + // create visual tree instance + var visualTreeInstance = default(IVisualTree); + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = visualTreeType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var parameters = constructor.GetParameters(); + var hubProperties = _componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var contextIdProperty = pageContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.PropertyType == typeof(IComponentId)) + .FirstOrDefault(); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? _componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? _httpServerContext : + parameter.ParameterType == typeof(IPageContext) ? pageContext : + parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(pageContext) : + hubProperties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(_componentHub) ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is IVisualTree visualTree) + { + visualTreeInstance = visualTree; + } + } + } + else + { + visualTreeInstance = Activator.CreateInstance(visualTreeType) as IVisualTree; + } // execute the cached delegate del.DynamicInvoke(renderContext, visualTreeInstance); From 14b6eb58e07f9f4b26f3f75feef8b306cb9f1c82 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 21 Dec 2024 09:04:33 +0100 Subject: [PATCH 107/162] refactor: clean up code --- .../WebComponent/ComponentHub.cs | 11 +++--- .../WebComponent/IComponentHub.cs | 5 ++- src/WebExpress.WebCore/WebEx.cs | 2 +- .../WebFragment/FragmentManager.cs | 39 +++++++++++++++---- .../WebFragment/IFragmentManager.cs | 19 ++++++++- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index d24aa21..97e4760 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -303,13 +303,14 @@ public IComponentManager GetComponentManager(string id) /// /// Returns a component based on its type. /// - /// The component class. + /// The component class. /// The instance of the component or null. - public T GetComponent() where T : IComponentManager + public TComponentManager GetComponentManager() + where TComponentManager : IComponentManager { - return (T)_dictionary.Values + return (TComponentManager)_dictionary.Values .SelectMany(x => x) - .Where(x => x.ComponentClass == typeof(T)) + .Where(x => x.ComponentClass == typeof(TComponentManager)) .Select(x => x.ComponentInstance) .FirstOrDefault(); } @@ -317,7 +318,7 @@ public T GetComponent() where T : IComponentManager /// /// Discovers and registers the components from the specified plugin. /// - /// A plugin context that contain the components. + /// A plugin context that contain the components. internal void Register(IPluginContext pluginContext) { // the plugin has already been registered diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index c1cc45f..a0ac758 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -166,8 +166,9 @@ public interface IComponentHub : IComponentManager /// /// Returns a component based on its type. /// - /// The component class. + /// The component class. /// The instance of the component. - T GetComponent() where T : IComponentManager; + TComponentManager GetComponentManager() + where TComponentManager : IComponentManager; } } diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index d1325cc..ae48864 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -309,7 +309,7 @@ public static IComponentManager GetComponent(string id) /// The instance of the component or null. public static T GetComponent() where T : IComponentManager { - return _componentHub.GetComponent(); + return _componentHub.GetComponentManager(); } /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index fb13908..1fcb957 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -101,7 +101,7 @@ private void Register(IApplicationContext applicationContext) /// Registers pages for a given plugin and application context. /// /// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext.Assembly; @@ -359,12 +359,12 @@ public IEnumerable GetFragments(Type fragmentType) /// /// Returns all fragment contexts that belong to a given fragment type. /// - /// The fragment type.. + /// The fragment type. /// The application context. /// An enumeration of the filtered fragment contexts. - public IEnumerable GetFragments(IApplicationContext applicationContext) where T : IFragmentBase + public IEnumerable GetFragments(IApplicationContext applicationContext) where TFragment : IFragmentBase { - return GetFragments(applicationContext, typeof(T)); + return GetFragments(applicationContext, typeof(TFragment)); } /// @@ -389,13 +389,15 @@ public IEnumerable GetFragments(IApplicationContext applicatio /// /// Returns all fragment contexts that belong to a given application. /// - /// The section where the fragment is embedded. - /// The scope where the fragment is embedded. + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. /// The application context. /// An enumeration of the filtered fragment contexts. - public IEnumerable GetFragments(IApplicationContext applicationContext) where S : ISection where T : IScope + public IEnumerable GetFragments(IApplicationContext applicationContext) + where TSection : ISection + where TScope : IScope { - return GetFragments(applicationContext, typeof(S), typeof(T)); + return GetFragments(applicationContext, typeof(TSection), typeof(TScope)); } /// @@ -419,6 +421,27 @@ public IEnumerable GetFragments(IApplicationContext applicatio .Select(x => x.FragmentContext); } + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The fragment type. + /// The section where the fragment is embedded. + /// The application context. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) + where TFragment : IFragmentBase + where TSection : ISection + { + foreach (var scope in scopes) + { + foreach (var item in GetFragments(applicationContext, typeof(TSection), scope)) + { + yield return item; + } + } + } + /// /// Returns all fragment contexts that belong to a given application. /// diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index 5472850..5664934 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -49,7 +49,8 @@ public interface IFragmentManager : IComponentManager /// The fragment type.. /// The application context. /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments(IApplicationContext applicationContext) where TFragment : IFragmentBase; + IEnumerable GetFragments(IApplicationContext applicationContext) + where TFragment : IFragmentBase; /// /// Returns all fragment contexts that belong to a given fragment type. @@ -66,7 +67,9 @@ public interface IFragmentManager : IComponentManager /// The scope where the fragment is embedded. /// The application context. /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments(IApplicationContext applicationContext) where TSection : ISection where TScope : IScope; + IEnumerable GetFragments(IApplicationContext applicationContext) + where TSection : ISection + where TScope : IScope; /// /// Returns all fragment contexts that belong to a given application. @@ -77,6 +80,18 @@ public interface IFragmentManager : IComponentManager /// An enumeration of the filtered fragment contexts. IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope); + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The fragment type. + /// The section where the fragment is embedded. + /// The application context. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) + where TFragment : IFragmentBase + where TSection : ISection; + /// /// Returns all fragment contexts that belong to a given application. /// From 4f2ea074a99fef661c0f3a233b3438b671adefc2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 21 Dec 2024 23:13:44 +0100 Subject: [PATCH 108/162] feat: enhance fragment and render logic --- .../Fixture/UnitTestFixture.cs | 1 - .../Manager/UnitTestFragmentManager.cs | 24 ++ .../WebFragment/FragmentContext.cs | 9 + .../WebFragment/FragmentManager.cs | 38 ++- .../WebFragment/IFragmentBase.cs | 6 +- .../WebFragment/IFragmentManager.cs | 6 +- .../WebFragment/Model/FragmentItem.cs | 313 +++++++++--------- .../WebPage/IVisualTreeContext.cs | 5 + .../WebPage/VisualTreeContext.cs | 14 +- .../WebStatusPage/StatusPageManager.cs | 2 +- 10 files changed, 240 insertions(+), 178 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index 1362b35..bc3cc43 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -175,7 +175,6 @@ public static WebMessage.HttpContext CreateHttpContextMock(string content = "") featureCollection.Set(requestIdentifierFeature); featureCollection.Set(connectionFeature); - var componentManager = CreateComponentHubMock(); var context = new WebMessage.HttpContext(featureCollection, CreateHttpServerContextMock()); return context; diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 857e29f..d4e73f4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -86,6 +86,30 @@ public void Id(Type applicationType, Type fragmentType, string id) Assert.Contains(id, fragment.Select(x => x.FragmentId?.ToString())); } + /// + /// Test the get fragment function of the fragment. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(IScope), 2)] + [InlineData(typeof(TestApplicationA), typeof(TestScopeA), 1)] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), 1)] + [InlineData(typeof(TestApplicationB), typeof(IScope), 2)] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), 1)] + public void GetFragments(Type applicationType, Type scopeType, int count) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); + + // test execution + var fragments = componentHub.FragmentManager.GetFragments(application, renderContext?.PageContext?.Scopes).ToList(); + + Assert.NotNull(fragments); + Assert.NotEmpty(fragments); + Assert.Equal(count, fragments.Count); + } + /// /// Test the process function of the fragment. /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs index 5350721..d69438c 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs @@ -54,5 +54,14 @@ public class FragmentContext : IFragmentContext public FragmentContext() { } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Fragment: {FragmentId}"; + } } } diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 1fcb957..0e4261c 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -409,35 +409,49 @@ public IEnumerable GetFragments(IApplication /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope) { + scope = scope ?? typeof(IScope); + return _dictionary.Values .SelectMany(x => x) .Where(x => x.Key == applicationContext) .SelectMany(x => x.Value) - .Where(x => x.Key == section) + .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) - .Where(x => x.Key == scope) + .Where(x => x.Key == scope || scope.IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) .OrderBy(x => x.Order) .Select(x => x.FragmentContext); } /// - /// Returns all fragment contexts that belong to a given application. + /// Returns all fragments that belong to a given application. /// /// The fragment type. /// The section where the fragment is embedded. /// The application context. /// The scopes where the fragment is embedded. - /// An enumeration of the filtered fragment contexts. - public IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) + /// An enumeration of the filtered fragments. + public IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) where TFragment : IFragmentBase where TSection : ISection { - foreach (var scope in scopes) + var effectiveScopes = (scopes?.Any() == true) ? scopes : [typeof(IScope)]; + + foreach (var scope in effectiveScopes) { - foreach (var item in GetFragments(applicationContext, typeof(TSection), scope)) + foreach (var item in _dictionary.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == typeof(TSection) || typeof(TSection).IsAssignableFrom(x.Key)) + .SelectMany(x => x.Value) + .Where(x => x.Key == scope || scope.IsAssignableFrom(x.Key)) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == typeof(TFragment) || typeof(TFragment).IsAssignableFrom(x.FragmentClass)) + .OrderBy(x => x.Order) + ) { - yield return item; + yield return item.CreateInstance(); } } } @@ -451,7 +465,9 @@ public IEnumerable GetFragments(IApplicat /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, IEnumerable scopes) { - foreach (var scope in scopes) + var effectiveScopes = (scopes?.Any() == true) ? scopes : [typeof(IScope)]; + + foreach (var scope in effectiveScopes) { foreach (var item in GetFragments(applicationContext, section, scope)) { @@ -469,13 +485,13 @@ public IEnumerable GetFragments(IApplicationContext applicatio /// An enumeration of HTML nodes representing the rendered fragments. public IEnumerable Render(TRenderContext renderContext, Type section) where TRenderContext : IRenderContext { - var scopes = renderContext?.PageContext?.Scopes ?? []; + var scopes = renderContext?.PageContext?.Scopes ?? [typeof(IScope)]; var items = _dictionary.Values .SelectMany(x => x) .Where(x => x.Key == renderContext?.PageContext?.ApplicationContext) .SelectMany(x => x.Value) - .Where(x => x.Key == section) + .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) .Where(x => scopes.Any(y => y.IsAssignableFrom(x.Key))) .SelectMany(x => x.Value) diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs b/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs index 1a0083a..99e4524 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentBase.cs @@ -1,9 +1,11 @@ -namespace WebExpress.WebCore.WebFragment +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebFragment { /// /// Defines the base interface for all fragments in the WebExpress framework. /// - public interface IFragmentBase + public interface IFragmentBase : IComponent { } } diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index 5664934..2986e5a 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -81,14 +81,14 @@ IEnumerable GetFragments(IApplicationContext IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope); /// - /// Returns all fragment contexts that belong to a given application. + /// Returns all fragments that belong to a given application. /// /// The fragment type. /// The section where the fragment is embedded. /// The application context. /// The scopes where the fragment is embedded. - /// An enumeration of the filtered fragment contexts. - IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) + /// An enumeration of the filtered fragments. + public IEnumerable GetFragments(IApplicationContext applicationContext, IEnumerable scopes) where TFragment : IFragmentBase where TSection : ISection; diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index 1bbcbaa..b0f6100 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -1,151 +1,162 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebHtml; -using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebFragment.Model -{ - /// - /// Fragments are components that can be integrated into pages to dynamically expand functionalities. - /// - internal class FragmentItem : IDisposable - { - private IComponent _instance; - private readonly IComponentHub _componentHub; - private readonly IHttpServerContext _httpServerContext; - private static readonly Dictionary _delegateCache = []; - - /// - /// Returns the context of the associated plugin. - /// - public IPluginContext PluginContext { get; set; } - - /// - /// Returns the application context. - /// - public IApplicationContext ApplicationContext { get; set; } - - /// - /// Returns the fragment context. - /// - public IFragmentContext FragmentContext { get; set; } - - /// - /// The type of fragment. - /// - public Type FragmentClass { get; set; } - - /// - /// Returns the section. - /// - public Type Section { get; set; } - - /// - /// Returns the scope. - /// - public Type Scope { get; set; } - - /// - /// Returns the conditions that must be met for the component to be active. - /// - public ICollection Conditions { get; set; } - - /// - /// The order of the fragment. - /// - public int Order { get; set; } - - /// - /// Determines whether the component is created once and reused on each execution. - /// - public bool Cache { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The fragment manager responsible for managing web fragments. - /// The context of the HTTP server. - public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerContext) - { - _componentHub = componentHub; - _httpServerContext = httpServerContext; - } - /// - /// Processes the fragments for a given section within the specified render context. - /// - /// The context in which rendering occurs. - /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - public IHtmlNode Render(T renderContext) where T : IRenderContext - { - var instance = _instance; - - instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); - - if (Cache) - { - _instance = instance; - } - - if (CheckControl(renderContext)) - { - if (!_delegateCache.TryGetValue(FragmentClass, out var del)) - { - // create and compile the expression - var renderContextType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; - var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); - var renderMethod = FragmentClass.GetMethod("Render", [renderContextType]); - var callProzessMethod = Expression.Call - ( - Expression.Constant(instance), - renderMethod, - renderContextParam - ); - var lambda = Expression.Lambda(callProzessMethod, renderContextParam) - .Compile(); - - _delegateCache[FragmentClass] = lambda; - del = lambda; - } - - // execute the cached delegate - var html = del.DynamicInvoke(renderContext) as IHtmlNode; - - return html; - } - - return null; - } - - /// - /// Checks the component to see if they are displayed or disabled. - /// - /// The context in which checking occurs. - /// True if the fragment is active, false otherwise. - private bool CheckControl(T renderContext) where T : IRenderContext - { - return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); - } - - /// - /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. - /// - public void Dispose() - { - } - - /// - /// Convert the resource element to a string. - /// - /// The resource element in its string representation. - public override string ToString() - { - return $"Fragment: '{FragmentContext.FragmentId}'"; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment.Model +{ + /// + /// Fragments are components that can be integrated into pages to dynamically expand functionalities. + /// + internal class FragmentItem : IDisposable + { + private IFragmentBase _instance; + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private static readonly Dictionary _delegateCache = []; + + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; set; } + + /// + /// Returns the fragment context. + /// + public IFragmentContext FragmentContext { get; set; } + + /// + /// The type of fragment. + /// + public Type FragmentClass { get; set; } + + /// + /// Returns the section. + /// + public Type Section { get; set; } + + /// + /// Returns the scope. + /// + public Type Scope { get; set; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + public ICollection Conditions { get; set; } + + /// + /// The order of the fragment. + /// + public int Order { get; set; } + + /// + /// Determines whether the component is created once and reused on each execution. + /// + public bool Cache { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The context of the HTTP server. + public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + _httpServerContext = httpServerContext; + } + + /// + /// Create the instance of the component. + /// + public TFragment CreateInstance() where TFragment : IFragmentBase + { + var instance = _instance; + + instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + + if (Cache) + { + _instance = instance; + } + + return (TFragment)instance; + } + + /// + /// Processes the fragments for a given section within the specified render context. + /// + /// The context in which rendering occurs. + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + public IHtmlNode Render(T renderContext) where T : IRenderContext + { + var instance = CreateInstance(); + + if (CheckControl(renderContext)) + { + if (!_delegateCache.TryGetValue(FragmentClass, out var del)) + { + // create and compile the expression + var renderContextType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; + var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); + var renderMethod = FragmentClass.GetMethod("Render", [renderContextType]); + var callProzessMethod = Expression.Call + ( + Expression.Constant(instance), + renderMethod, + renderContextParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam) + .Compile(); + + _delegateCache[FragmentClass] = lambda; + del = lambda; + } + + // execute the cached delegate + var html = del.DynamicInvoke(renderContext) as IHtmlNode; + + return html; + } + + return null; + } + + /// + /// Checks the component to see if they are displayed or disabled. + /// + /// The context in which checking occurs. + /// True if the fragment is active, false otherwise. + private bool CheckControl(T renderContext) where T : IRenderContext + { + return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Convert the resource element to a string. + /// + /// The resource element in its string representation. + public override string ToString() + { + return $"Fragment: '{FragmentContext.FragmentId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs index b328c46..3532e3e 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs @@ -17,5 +17,10 @@ public interface IVisualTreeContext /// The uri of the request. /// UriResource Uri { get; } + + /// + /// Return or sets the render context. + /// + IRenderContext RenderContext { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs index 749661d..76f7bc8 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -11,29 +11,25 @@ public class VisualTreeContext : IVisualTreeContext /// /// Returns the request. /// - public Request Request { get; protected set; } + public Request Request => RenderContext?.Request; /// /// The uri of the request. /// - public UriResource Uri => Request?.Uri; + public UriResource Uri => RenderContext?.Request?.Uri; /// - /// Initializes a new instance of the class. + /// Return or sets the render context. /// - /// The request associated with the rendering context. - public VisualTreeContext(Request request) - { - Request = request; - } + public IRenderContext RenderContext { get; protected set; } /// /// Initializes a new instance of the class. /// /// The context to copy./param> public VisualTreeContext(IRenderContext context) - : this(context?.Request) { + RenderContext = context; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 1c31298..f35d7f2 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -359,7 +359,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon del.DynamicInvoke(renderContext, visualTreeInstance); var response = ComponentActivator.CreateInstance(statusPageItem.StatusResponse, _httpServerContext, _componentHub, new StatusMessage(message)); - var content = visualTreeInstance.Render(new VisualTreeContext(request))?.ToString(); + var content = visualTreeInstance.Render(new VisualTreeContext(renderContext))?.ToString(); response.Content = content; response.Header.ContentLength = content?.Length ?? 0; From 880a4e7f6c2ef0a02c354f94b75f013ff9115c75 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Mon, 23 Dec 2024 22:42:12 +0100 Subject: [PATCH 109/162] remove IDisposable from IComponent --- src/WebExpress.WebCore/WebComponent/IComponent.cs | 6 ++---- src/WebExpress.WebCore/WebEvent/Model/EventItem.cs | 7 +++++-- .../WebIdentity/Model/IdentityPermissionItem.cs | 5 ++++- .../WebIdentity/Model/IdentityRoleItem.cs | 5 ++++- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/WebExpress.WebCore/WebComponent/IComponent.cs b/src/WebExpress.WebCore/WebComponent/IComponent.cs index 9fd421c..7954ea8 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponent.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponent.cs @@ -1,11 +1,9 @@ -using System; - -namespace WebExpress.WebCore.WebComponent +namespace WebExpress.WebCore.WebComponent { /// /// Interface of a component. /// - public interface IComponent : IDisposable + public interface IComponent { } diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs index 180a4d4..16c78f1 100644 --- a/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventItem.cs @@ -13,7 +13,7 @@ namespace WebExpress.WebCore.WebEvent.Model internal class EventItem : IDisposable { private readonly IComponentHub _componentHub; - private readonly IDisposable _instance; + private readonly IComponent _instance; /// /// Returns the associated plugin context. @@ -110,7 +110,10 @@ public void Process(object sender, IEventArgument eventArgument) /// public void Dispose() { - _instance?.Dispose(); + if (_instance is IDisposable disposable) + { + disposable.Dispose(); + } } /// diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs index 51881f6..85f7e18 100644 --- a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionItem.cs @@ -69,7 +69,10 @@ public IdentityPermissionItem(IComponentHub componentHub, IHttpServerContext htt /// public void Dispose() { - Instance?.Dispose(); + if (Instance is IDisposable disposable) + { + disposable.Dispose(); + } } /// diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs index 6b9b2d4..488530b 100644 --- a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleItem.cs @@ -69,7 +69,10 @@ public IdentityRoleItem(IComponentHub componentHub, IHttpServerContext httpServe /// public void Dispose() { - Instance?.Dispose(); + if (Instance is IDisposable disposable) + { + disposable.Dispose(); + } } /// From b2b9aaa1cca95247cd97702306c4e3a4f643f8bd Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 24 Dec 2024 08:16:32 +0100 Subject: [PATCH 110/162] remove IDisposable --- src/WebExpress.WebCore/WebApplication/IApplication.cs | 5 ++--- src/WebExpress.WebCore/WebPlugin/IPlugin.cs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/WebExpress.WebCore/WebApplication/IApplication.cs b/src/WebExpress.WebCore/WebApplication/IApplication.cs index d4e56d6..4ea4ddc 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplication.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplication.cs @@ -1,12 +1,11 @@ -using System; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebApplication { /// /// This interface represents an application. /// - public interface IApplication : IComponent, IDisposable + public interface IApplication : IComponent { /// /// Called when the application starts working. The call is concurrent. diff --git a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs index 2e364f4..bcc96d2 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPlugin.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPlugin.cs @@ -1,12 +1,11 @@ -using System; -using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebComponent; namespace WebExpress.WebCore.WebPlugin { /// /// This interface represents a plugin. /// - public interface IPlugin : IComponent, IDisposable + public interface IPlugin : IComponent { /// /// Called when the plugin starts working. The call is concurrent. From 831235639ac4d20ffadd7610ae405ea4f8c8d168 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 28 Dec 2024 19:52:16 +0100 Subject: [PATCH 111/162] add system plugin functionality --- .../Fixture/UnitTestFixture.cs | 2 +- .../Internationalization/de | 1 - .../Internationalization/en | 1 - .../WebAsset/AssetManager.cs | 2 +- src/WebExpress.WebCore/WebEx.cs | 32 ++-------- .../WebPlugin/PluginManager.cs | 61 +++++++++++++++---- 6 files changed, 56 insertions(+), 43 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index bc3cc43..e7fafca 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -67,7 +67,7 @@ public static ComponentHub CreateComponentHubMock() var componentHub = (ComponentHub)ctorComponentHub.Invoke([CreateHttpServerContextMock()]); // set static field in the webex class - var type = typeof(WebEx); + var type = typeof(WebEx); var field = type.GetField("_componentHub", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, componentHub); diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index a21456b..f232ba4 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -66,7 +66,6 @@ pluginmanager.pluginmanager.label=Plugin Manager: pluginmanager.pluginmanager.system=Systemplugin: '{0}' pluginmanager.pluginmanager.custom=Benutzerdefiniertes Plugin: '{0}' pluginmanager.pluginmanager.unfulfilleddependencies=Plugin mit unerfüllten Abhängigkeiten: '{0}' -pluginmanager.plugin.initialization=Das Plugin '{0}' wurde initialisiert. pluginmanager.plugin.processing.start=Das Plugin '{0}' wird ausgeführt. pluginmanager.plugin.processing.end=Die Ausführung des Plugin '{0}' wurde beendet. pluginmanager.fulfilleddependencies=Das Plugin '{0}' erfüllt alle Abhängigkeiten. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 97d43df..fdb1210 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -66,7 +66,6 @@ pluginmanager.pluginmanager.label=Plugin manager: pluginmanager.pluginmanager.system=System plugin: '{0}' pluginmanager.pluginmanager.custom=custom plugin: '{0}' pluginmanager.pluginmanager.unfulfilleddependencies=Plugin with unfulfilled dependencies: '{0}' -pluginmanager.plugin.initialization=The plugin '{0}' has been initialized. pluginmanager.plugin.processing.start=The plugin '{0}' is running. pluginmanager.plugin.processing.end=The running of the plugin '{0}' has been stopped. pluginmanager.fulfilleddependencies=The plugin '{0}' fulfills all dependencies. diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 60973d3..4bb1a09 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -130,7 +130,7 @@ private void Register(IApplicationContext applicationContext) /// Registers resources for a given plugin and application context. /// /// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index ae48864..38a530b 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -16,29 +16,12 @@ namespace WebExpress.WebCore { - /// - /// The class provides a web server application for WebExpress with a default component hub. - /// - public class WebEx : WebEx - { - /// - /// Creates and returns a new instance of . - /// - /// The HTTP server context used to initialize the component manager. - /// A new instance of . - protected override IComponentHub CreateComponentManager(IHttpServerContext httpServerContext) - { - return ComponentActivator.CreateInstance(httpServerContext); - } - } - /// /// The class provides a web server application for WebExpress. /// - /// The type of the component hub, which must implement . - public abstract class WebEx where T : class, IComponentHub + public sealed class WebEx { - private static T _componentHub; + private static IComponentHub _componentHub; private HttpServer _httpServer; /// @@ -54,7 +37,7 @@ public abstract class WebEx where T : class, IComponentHub /// /// Returns the component hub. /// - public static T ComponentHub => _componentHub; + public static IComponentHub ComponentHub => _componentHub; /// /// Running the application. @@ -217,7 +200,7 @@ private void Initialization(string args, string configFile) Config = config }; - _componentHub = CreateComponentManager(_httpServer.HttpServerContext) as T; + _componentHub = ComponentActivator.CreateInstance(_httpServer.HttpServerContext); // start logging _httpServer.HttpServerContext.Log.Begin(config.Log); @@ -311,12 +294,5 @@ public static T GetComponent() where T : IComponentManager { return _componentHub.GetComponentManager(); } - - /// - /// Creates and returns a new instance of . - /// - /// The HTTP server context used to initialize the component manager. - /// A new instance of . - protected abstract T CreateComponentManager(IHttpServerContext httpServerContext); } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index f4d5df5..5f710f3 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -165,6 +165,51 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex try { + // system plugins without plugin class (e.g. webexpress.webui) + if (assembly.GetCustomAttribute() != null) + { + var id = new ComponentId(assembly.GetName().Name.ToLower()); + var pluginContext = new PluginContext() + { + Assembly = assembly, + PluginId = id, + PluginName = assembly.GetName().Name.ToLower(), + Manufacturer = assembly.GetCustomAttribute()?.Company, + Copyright = assembly.GetCustomAttribute()?.Copyright, + Version = assembly.GetCustomAttribute()?.InformationalVersion, + Host = _httpServerContext + }; + + if (!_dictionary.ContainsKey(id)) + { + _dictionary.Add(id, new PluginItem() + { + PluginLoadContext = loadContext, + PluginClass = typeof(IPlugin), + PluginContext = pluginContext, + Plugin = null, + Dependencies = null, + ApplicationTypes = [typeof(IApplication)] + }); + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress.webcore:pluginmanager.created", id) + ); + + OnAddPlugin(pluginContext); + } + else + { + _httpServerContext.Log.Warning + ( + I18N.Translate("webexpress.webcore:pluginmanager.duplicate", id) + ); + } + + plugins.Add(pluginContext); + } + foreach (var type in assembly .GetExportedTypes() .Where(x => x.IsClass && x.IsSealed) @@ -400,7 +445,7 @@ public IPluginContext GetPlugin(string pluginId) /// /// Returns a plugin context based on its id. /// - /// The type of the plugin. + /// The type of the plugin. /// The plugin context. public IPluginContext GetPlugin(Type plugin) { @@ -491,16 +536,10 @@ internal void Boot(IPluginContext pluginContext) return; } - //// initialize plugin - //pluginItem.Plugin.Initialization(pluginItem.PluginContext); - //HttpServerContext.Log.Debug - //( - // I18N.Translate - // ( - // "webexpress.webcore:pluginmanager.plugin.initialization", - // pluginItem.PluginContext.PluginId - // ) - //); + if (pluginItem.Plugin == null) + { + return; + } // run plugin concurrently Task.Run(() => From 81b5a6749827655c40638c7ee98b8837504ab558 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 28 Dec 2024 21:01:28 +0100 Subject: [PATCH 112/162] extend RenderContext to include current endpoint --- .../Fixture/UnitTestFixture.cs | 2 +- src/WebExpress.WebCore.Test/TestRenderContext.cs | 14 ++++++++++++-- src/WebExpress.WebCore/WebPage/IRenderContext.cs | 8 +++++++- src/WebExpress.WebCore/WebPage/PageManager.cs | 2 +- src/WebExpress.WebCore/WebPage/RenderContext.cs | 15 +++++++++++---- .../WebSettingPage/SettingPageManager.cs | 2 +- .../WebStatusPage/StatusPageManager.cs | 3 ++- 7 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index e7fafca..5c66546 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -190,7 +190,7 @@ public static RenderContext CrerateRenderContextMock(IApplicationContext applica { var request = CrerateRequestMock(); - return new RenderContext(CreratePageContextMock(applicationContext, scopes), request); + return new RenderContext(null, CreratePageContextMock(applicationContext, scopes), request); } /// /// Create a fake page context for unit testing. diff --git a/src/WebExpress.WebCore.Test/TestRenderContext.cs b/src/WebExpress.WebCore.Test/TestRenderContext.cs index 1e31ac7..5b84420 100644 --- a/src/WebExpress.WebCore.Test/TestRenderContext.cs +++ b/src/WebExpress.WebCore.Test/TestRenderContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; namespace WebExpress.WebCore.Test @@ -8,6 +9,11 @@ namespace WebExpress.WebCore.Test /// public class TestRenderContext : IRenderContext { + /// + /// Returns the endpoint associated with the rendering context. + /// + public IEndpoint Endpoint { get; protected set; } + /// /// Returns the page context. /// @@ -21,10 +27,14 @@ public class TestRenderContext : IRenderContext /// /// Initializes a new instance of the class. /// + /// The endpoint associated with the rendering context. /// >The page context. /// The request associated with the rendering context. - public TestRenderContext(IPageContext pageContext, Request request) + public TestRenderContext(IEndpoint endpoint, IPageContext pageContext, Request request) { + Endpoint = endpoint; + PageContext = pageContext; + Request = request; } } } diff --git a/src/WebExpress.WebCore/WebPage/IRenderContext.cs b/src/WebExpress.WebCore/WebPage/IRenderContext.cs index 08ece95..e55b918 100644 --- a/src/WebExpress.WebCore/WebPage/IRenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/IRenderContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebMessage; namespace WebExpress.WebCore.WebPage { @@ -7,6 +8,11 @@ namespace WebExpress.WebCore.WebPage /// public interface IRenderContext { + /// + /// Returns the endpoint associated with the rendering context. + /// + IEndpoint Endpoint { get; } + /// /// Returns the page context. /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 596f380..d35c679 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -68,7 +68,7 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon var pageInstance = CreatePageInstance(endpontContext as IPageContext); var pageType = pageInstance.GetType(); var pageContext = endpontContext as IPageContext; - var renderContext = new RenderContext(pageContext, request); + var renderContext = new RenderContext(pageInstance, pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); var visualTreeType = pageType.GetInterface(typeof(IPage<>).Name).GetGenericArguments()[0]; diff --git a/src/WebExpress.WebCore/WebPage/RenderContext.cs b/src/WebExpress.WebCore/WebPage/RenderContext.cs index b9f76a7..926eaae 100644 --- a/src/WebExpress.WebCore/WebPage/RenderContext.cs +++ b/src/WebExpress.WebCore/WebPage/RenderContext.cs @@ -1,4 +1,5 @@ using System.Globalization; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; @@ -29,6 +30,11 @@ public class RenderContext : IRenderContext /// public CultureInfo Culture => Request?.Culture; + /// + /// Returns the endpoint associated with the rendering context. + /// + public IEndpoint Endpoint { get; protected set; } + /// /// Initializes a new instance of the class. /// @@ -39,11 +45,12 @@ public RenderContext() /// /// Initializes a new instance of the class. /// - /// >The page context. + /// The endpoint associated with the rendering context. + /// The page context. /// The request associated with the rendering context. - public RenderContext(IPageContext pageContext, Request request) - : this() + public RenderContext(IEndpoint endpoint, IPageContext pageContext, Request request) { + Endpoint = endpoint; PageContext = pageContext; Request = request; } @@ -53,7 +60,7 @@ public RenderContext(IPageContext pageContext, Request request) /// /// The context to copy. public RenderContext(RenderContext context) - : this(context?.PageContext, context?.Request) + : this(context.Endpoint, context?.PageContext, context?.Request) { } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 92d3c4b..15a8183 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -73,7 +73,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe } else { - context = new RenderContext(pageContetx, request); + context = new RenderContext(settingPage, pageContetx, request); } settingPage.Process(context); diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index f35d7f2..7474a65 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -7,6 +7,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -294,7 +295,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon ); var pageType = pageInstance.GetType(); var pageContext = new PageContext(_componentHub.EndpointManager, null, request.Uri, new UriPathSegmentRoot()); - var renderContext = new RenderContext(pageContext, request); + var renderContext = new RenderContext(pageInstance as IEndpoint, pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); var visualTreeType = pageType.GetInterface(typeof(IStatusPage<>).Name).GetGenericArguments()[0]; From bdd35ef15566cc122164ce33d3500bfa331caaa2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 28 Dec 2024 21:28:59 +0100 Subject: [PATCH 113/162] fix: return ContentType as binary/octet-stream instead of ResponseNotFound --- src/WebExpress.WebCore/WebAsset/Asset.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore/WebAsset/Asset.cs b/src/WebExpress.WebCore/WebAsset/Asset.cs index 5423486..3487aa8 100644 --- a/src/WebExpress.WebCore/WebAsset/Asset.cs +++ b/src/WebExpress.WebCore/WebAsset/Asset.cs @@ -120,7 +120,8 @@ public Response Process(Request request) response.Header.ContentType = "video/mp4"; break; default: - return new ResponseNotFound(); + response.Header.ContentType = "binary/octet-stream"; + break; } _httpServerContext.Log.Debug(I18N.Translate From e247e4494103db9933d0d596e11227900929e155 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 12:24:20 +0100 Subject: [PATCH 114/162] refactor: update assignment logic for scope - changed logic to check if scope is assignable to x.Key instead of checking if scope can be assigned from x.Key --- .../Manager/UnitTestFragmentManager.cs | 5 ++--- src/WebExpress.WebCore/WebFragment/FragmentManager.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index d4e73f4..1399b34 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -90,10 +90,10 @@ public void Id(Type applicationType, Type fragmentType, string id) /// Test the get fragment function of the fragment. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(IScope), 2)] + [InlineData(typeof(TestApplicationA), typeof(IScope), 0)] [InlineData(typeof(TestApplicationA), typeof(TestScopeA), 1)] [InlineData(typeof(TestApplicationA), typeof(TestPageB), 1)] - [InlineData(typeof(TestApplicationB), typeof(IScope), 2)] + [InlineData(typeof(TestApplicationB), typeof(IScope), 0)] [InlineData(typeof(TestApplicationB), typeof(TestPageB), 1)] public void GetFragments(Type applicationType, Type scopeType, int count) { @@ -106,7 +106,6 @@ public void GetFragments(Type applicationType, Type scopeType, int count) var fragments = componentHub.FragmentManager.GetFragments(application, renderContext?.PageContext?.Scopes).ToList(); Assert.NotNull(fragments); - Assert.NotEmpty(fragments); Assert.Equal(count, fragments.Count); } diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 0e4261c..7c211f3 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -445,7 +445,7 @@ public IEnumerable GetFragments(IApplicationCont .SelectMany(x => x.Value) .Where(x => x.Key == typeof(TSection) || typeof(TSection).IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) - .Where(x => x.Key == scope || scope.IsAssignableFrom(x.Key)) + .Where(x => x.Key == scope || scope.IsAssignableTo(x.Key)) .SelectMany(x => x.Value) .Where(x => x.FragmentClass == typeof(TFragment) || typeof(TFragment).IsAssignableFrom(x.FragmentClass)) .OrderBy(x => x.Order) From 77299af7211b3f2af2adb4890dc0def9b57f95ec Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 13:06:59 +0100 Subject: [PATCH 115/162] chore: optimize and enhance log outputs --- .../Internationalization/de | 12 +++-- .../Internationalization/en | 7 ++- .../WebApplication/ApplicationManager.cs | 5 +++ .../WebEvent/EventManager.cs | 45 +++++++++++-------- .../WebFragment/FragmentManager.cs | 4 ++ src/WebExpress.WebCore/WebJob/JobManager.cs | 23 ---------- .../WebPackage/PackageManager.cs | 28 ++++++++++-- src/WebExpress.WebCore/WebPage/PageManager.cs | 23 ---------- .../WebResource/ResourceManager.cs | 23 ---------- .../WebRestAPI/RestApiManager.cs | 23 ---------- .../WebSession/SessionManager.cs | 12 ----- .../WebSitemap/SitemapManager.cs | 5 +++ .../WebStatusPage/StatusPageManager.cs | 33 +++++++++----- 13 files changed, 99 insertions(+), 144 deletions(-) diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index f232ba4..cc827e6 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -40,12 +40,13 @@ componentmanager.duplicate=Die Komponente '{0}' wurde bereits registriert. componentmanager.remove=Die Komponente '{0}' wurde entfernt. componentmanager.component=Komponenten: -logmanager.initialization:Der Logmanager wurde initialisiert. -logmanager.titel:Logmanager +logmanager.initialization=Der Logmanager wurde initialisiert. +logmanager.titel=Logmanager internationalizationmanager.initialization=Der Internationalisierungsmanager wurde initialisiert. internationalizationmanager.register=Das Plugin '{0}' wurde im Internationalizationmanager registriert. +packagemanager.titel=Paketmanager: packagemanager.initialization=Der Paketmanager wurde initialisiert. packagemanager.existing=Das Paket '{0}' ist im Katalog registriert. packagemanager.add=Das Paket '{0}' wird im Katalog neu aufgenommen. @@ -54,6 +55,7 @@ packagemanager.scan=Das Verzeichnis '{0}' wird auf neue Pakete gescannt. packagemanager.save=Der Katalog wird gespeichert. packagemanager.packagenotfound=Das Paket '{0}' wurde nicht im Dateisystem gefunden. packagemanager.boot.notfound=Das Plugin '{0}' ist nicht bekannt. +packagemanager.package=Package: '{0}' pluginmanager.initialization=Der Pluginmanager wurde initialisiert. pluginmanager.load={0}.dll wird geladen. Version = '{1}' @@ -102,13 +104,14 @@ restapimanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist ber restapimanager.resource=Seite: '{0}' für die Anwendung '{1}' restapimanager.methodnotsupported=Die Methode '{0}' wird nicht unterstützt. +statuspagemanager.titel=Statuspagemanager: statuspagemanager.initialization=Der Statuspagemanager wurde initialisiert. statuspagemanager.register=Der Status '{0}' wurde registriert und der Statusseite '{1}' zugewiesen. statuspagemanager.duplicat=Der Status '{0}' wurde bereits registriert. Die Statusseite '{1}' wird daher nicht verwendet. statuspagemanager.statuscodeless=Ein Statuscode wurde der Ressource '{1}' für die Anwendung '{0}' nicht zugewiesen. statuspagemanager.statuspage=Statuscode: '{0}' -sitemapmanager..titel=Sitemap: +sitemapmanager.titel=Sitemap: sitemapmanager.initialization=Der Sitemap-Manager wurde initialisiert. sitemapmanager.refresh=Die Sitemap wird neu aufgebaut. sitemapmanager.alreadyassigned=Der Knoten der Sitemap '{0}' ist bereits zugewiesen. Die Ressource '{1}' wird nicht in die Sitemap aufgenommen. @@ -119,10 +122,12 @@ sitemapmanager.merge.error=Die beiden Sitemaps '{0}' und '{1}' konnten nicht gem sessionmanager.initialization=Der Sessionmanager wurde initialisiert. +eventmanager.titel=Eventmanager: eventmanager.initialization=Der Eventmanager wurde initialisiert. eventmanager.register=Der Eventhandler '{0}' wurde der Anwendung '{1}' zugewiesen und im Eventmanager registriert. eventmanager.duplicate=Der Eventhandler '{0}' wurde bereits in der Anwendung '{1}' registriert. eventmanager.eventless=Der Eventhandler '{0}' besitzt keine Angaben zu einem Event. +eventmanager.handler=Eventhandler: '{0}' für die Anwendung '{1}'. jobmanager.initialization=Der Jobmanager wurde initialisiert. jobmanager.register=Der Job '{0}' wurde der Anwendung '{1}' zugewiesen und im Jobmanager registriert. @@ -150,4 +155,3 @@ identitymanager.duplicaterole=Die Rolle '{0}' wurde bereits in der Anwendung '{1 resource.variable.duplicate=Variable '{0}' bereits vorhanden! resource.file={0}: Datei '{1}' wurde geladen. - diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index fdb1210..b4cbeac 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -46,6 +46,7 @@ logmanager.titel:Log manager internationalizationmanager.initialization=The internationalization manager has been initialized. internationalizationmanager.register=The plugin '{0}' is registered in the internationalization manager. +packagemanager.titel=Package manager: packagemanager.initialization=The package manager has been initialized. packagemanager.existing=The package '{0}' is registered in the catalog. packagemanager.add=Package '{0}' is added to the catalog. @@ -54,6 +55,7 @@ packagemanager.scan=The directory '{0}' is scanned for new packages. packagemanager.save=The catalog is saved. packagemanager.packagenotfound=The package '{0}' was not found in the file system. packagemanager.boot.notfound=The plugin '{0}' is unknown. +packagemanager.package=Package: '{0}' pluginmanager.initialization=The plugin manager has been initialized. pluginmanager.load={0}.dll is loading. Version = '{1}' @@ -102,6 +104,7 @@ restapimanager.addresource.duplicate=The REST API '{0}' of application '{1}' has restapimanager.resource=REST API: '{0}' for application '{1}' restapimanager.methodnotsupported=The method '{0}' is not supported. +statuspagemanager.titel=Status page manager: statuspagemanager.initialization=The status page manager has been initialized. statuspagemanager.register=The status '{0}' has been registered and assigned to the status page '{1}'. statuspagemanager.duplicat=The status '{0}' has already been registered. Therefore, the status page '{1}' is not used. @@ -119,10 +122,12 @@ sitemapmanager.merge.error=The two sitemaps '{0}' and '{1}' could not be merged. sessionmanager.initialization=The session manager has been initialized. +eventmanager.titel=Event manager: eventmanager.initialization=The event manager has been initialized. eventmanager.register=The event handler '{0}' has been registered in the application '{1}'. eventmanager.duplicate=The event handler '{0}' has already been registered. Therefore, the application '{1}' is not used. eventmanager.eventless=The event handler '{0}' does not have any information about the event. +eventmanager.handler=Event handler: '{0}' for application '{1}'. jobmanager.initialization=The schedule manager has been initialized. jobmanager.register=The job '{0}' has been registered in the application '{1}'. @@ -149,4 +154,4 @@ identitymanager.registerrole=The role '{0}' has been assigned to the application identitymanager.duplicaterole=The role '{0}' has already been registered in the application '{1}'. resource.variable.duplicate=Variable '{0}' already exists! -resource.file={0}: File '{1}' has been loaded. \ No newline at end of file +resource.file={0}: File '{1}' has been loaded. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 6c73a0b..0d2f711 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -402,6 +402,11 @@ private void OnRemovePlugin(object sender, IPluginContext e) /// private void Log() { + if (!Applications.Any()) + { + return; + } + using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List { diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index d421c0b..b0eb459 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -6,6 +6,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebEvent.Model; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebEvent @@ -139,7 +140,7 @@ private void Register(IApplicationContext applicationContext) /// Registers resources for a given plugin and application context. /// /// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -225,6 +226,8 @@ private void Register(IPluginContext pluginContext, IEnumerable @@ -332,26 +335,30 @@ private void OnAddApplication(object sender, IApplicationContext e) } /// - /// Information about the component is collected and prepared for output in the event. + /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { - //foreach (var scheduleItem in GetScheduleItems(pluginContext)) - //{ - // output.Add - // ( - // string.Empty.PadRight(deep) + - // I18N.Translate - // ( - // "webexpress.webcore:eventmanager.job", - // scheduleItem.JobId, - // scheduleItem.ModuleContext - // ) - // ); - //} + if (!EventHandlers.Any()) + { + return; + } + + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { + I18N.Translate("webexpress.webcore:eventmanager.titel") + }; + + foreach (var eventHandlerContext in EventHandlers) + { + list.Add + ( + I18N.Translate("webexpress.webcore:eventmanager.handler", eventHandlerContext.EventId, eventHandlerContext.ApplicationContext?.ApplicationId) + ); + } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 7c211f3..29bf526 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -505,6 +505,10 @@ public IEnumerable Render(TRenderContext renderContex /// private void Log() { + if (!Fragments.Any()) + { + return; + } using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 5df6ee5..0ccebd5 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -424,29 +424,6 @@ public IJobContext GetJob(IApplicationContext applicationContext, Type jobType) .FirstOrDefault(x => x.JobContext.ApplicationContext == applicationContext && x.JobClass == jobType)?.JobContext; } - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - foreach (var scheduleItem in Jobs.Where(x => x.PluginContext == pluginContext)) - { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate - ( - "webexpress.webcore:jobmanager.job", - scheduleItem.JobId, - scheduleItem.ApplicationContext - ) - ); - } - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index 9077e5a..133cee6 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -12,6 +12,7 @@ using System.Xml.Serialization; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPackage.Model; using WebExpress.WebCore.WebPlugin; @@ -320,6 +321,8 @@ private void LoadCatalog() //Catalog.Packages.RemoveAll(x => !x.System); Catalog.Packages.AddRange(items.Packages); } + + Log(); } /// @@ -465,11 +468,28 @@ private void OnRemovePackage(PackageCatalogItem item) /// /// Information about the component is collected and prepared for output in the log. /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) + private void Log() { + if (!Catalog.Packages.Any()) + { + return; + } + + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { + I18N.Translate("webexpress.webcore:packagemanager.titel") + }; + + foreach (var package in Catalog.Packages) + { + list.Add + ( + I18N.Translate("webexpress.webcore:packagemanager.package", package.Id) + ); + } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index d35c679..b582def 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -561,29 +561,6 @@ private void OnRemoveApplication(object sender, IApplicationContext e) Remove(e); } - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - foreach (var resourcenItem in GetPageItems(pluginContext)) - { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate - ( - "webexpress.webcore:pagemanager.resource", - resourcenItem?.PageContext?.EndpointId, - string.Join(",", resourcenItem?.PageContext?.ApplicationContext?.ApplicationId) - ) - ); - } - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 1f683fa..fffdbb5 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -460,29 +460,6 @@ private void OnAddApplication(object sender, IApplicationContext e) Register(e); } - /// - /// Information about the component is collected and prepared for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - foreach (var resourcenItem in GetResorceItems(pluginContext)) - { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate - ( - "webexpress.webcore:resourcemanager.resource", - resourcenItem?.ResourceContext?.EndpointId, - string.Join(",", resourcenItem.ResourceContext?.ApplicationContext?.ApplicationId) - ) - ); - } - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 13fecfc..c2b9c8a 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -527,29 +527,6 @@ private void OnAddApplication(object sender, IApplicationContext e) Register(e); } - /// - /// Collects and prepares information about the component for output in the log. - /// - /// The plugin context. - /// A list of log entries. - /// The depth of the log. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - foreach (var resourcenItem in GetRestApiItems(pluginContext)) - { - output.Add - ( - string.Empty.PadRight(deep) + - I18N.Translate - ( - "webexpress.webcore:restapimanager.resource", - resourcenItem?.RestApiContext?.EndpointId, - string.Join(",", resourcenItem?.RestApiContext?.ApplicationContext?.ApplicationId) - ) - ); - } - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index ffe6d43..b839be1 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; -using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSession.Model; namespace WebExpress.WebCore.WebSession @@ -75,16 +73,6 @@ public Session GetSession(Request request) return session; } - /// - /// Collects and prepares information about the component for output in the log. - /// - /// The context of the plugin. - /// A list of log entries. - /// The shaft deep. - public void PrepareForLog(IPluginContext pluginContext, IList output, int deep) - { - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 6af1d86..37a7a3e 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -447,6 +447,11 @@ private static bool IsMatched(SitemapNode node, string pathSegement) /// private void Log() { + if (!SiteMap.Any()) + { + return; + } + using var frame = new LogFrameSimple(_httpServerContext.Log); var list = new List { diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 7474a65..e348a71 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -476,18 +477,26 @@ private void OnAddApplication(object sender, IApplicationContext e) /// private void Log() { - //foreach (var statusCode in _dictionary.GetStatusPageContexts(pluginContext).Select(x => x.StatusCode)) - //{ - // output.Add - // ( - // string.Empty.PadRight(4) + - // I18N.Translate - // ( - // "webexpress.webcore:statuspagemanager.statuspage", - // statusCode - // ) - // ); - //} + if (!StatusPages.Any()) + { + return; + } + + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { + I18N.Translate("webexpress.webcore:statuspagemanager.titel") + }; + + foreach (var statusPage in StatusPages) + { + list.Add + ( + I18N.Translate("webexpress.webcore:statuspagemanager.statuspage", statusPage.StatusCode) + ); + } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); } /// From ab0395e313abeabbdbc351665112fd41e811d643 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 13:46:09 +0100 Subject: [PATCH 116/162] fix: consider only exact matches with elements in scopes - updated logic to ensure only exact matches with elements in scopes are considered. - removed handling of partial matches or derived types. --- src/WebExpress.WebCore/WebFragment/FragmentManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 29bf526..14f3bfb 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -417,7 +417,7 @@ public IEnumerable GetFragments(IApplicationContext applicatio .SelectMany(x => x.Value) .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) - .Where(x => x.Key == scope || scope.IsAssignableFrom(x.Key)) + .Where(x => x.Key == scope) .SelectMany(x => x.Value) .OrderBy(x => x.Order) .Select(x => x.FragmentContext); @@ -445,7 +445,7 @@ public IEnumerable GetFragments(IApplicationCont .SelectMany(x => x.Value) .Where(x => x.Key == typeof(TSection) || typeof(TSection).IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) - .Where(x => x.Key == scope || scope.IsAssignableTo(x.Key)) + .Where(x => x.Key == scope) .SelectMany(x => x.Value) .Where(x => x.FragmentClass == typeof(TFragment) || typeof(TFragment).IsAssignableFrom(x.FragmentClass)) .OrderBy(x => x.Order) @@ -493,7 +493,7 @@ public IEnumerable Render(TRenderContext renderContex .SelectMany(x => x.Value) .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) .SelectMany(x => x.Value) - .Where(x => scopes.Any(y => y.IsAssignableFrom(x.Key))) + .Where(x => scopes.Any(y => x.Key == y)) .SelectMany(x => x.Value) .OrderBy(x => x.Order); From 87f75264a9b2783bf312fd56fc50be6c89c8ecf3 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 14:31:08 +0100 Subject: [PATCH 117/162] docs: add reference to API documentation for WebExpress.WebUI --- README.md | 3 ++- docs/user-guide.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82e707f..031bb7b 100644 --- a/README.md +++ b/README.md @@ -27,9 +27,10 @@ The current binaries are available for download [here](https://github.com/ReneSc # Start If you're looking to get started with `WebExpress`, we would recommend using the following documentation. It can help you understand the platform. -- [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) - [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) - [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) +- [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) +- [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) # Learning The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. diff --git a/docs/user-guide.md b/docs/user-guide.md index 8aaffdc..b927a5e 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -9,5 +9,7 @@ To get started with WebExpress.WebCore, use the following guides: - [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) - [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) +- [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) +- [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) We hope you enjoy using `WebExpress.WebCore` and find it valuable for your projects. Happy coding! From 4fa2cca0d0bdd609ddec1992267f2cc698f4684d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 18:30:09 +0100 Subject: [PATCH 118/162] feat: Enable StatusPages to set VisualTree and general code improvements - StatusPages can now define their VisualTree, similar to how Pages do --- .../TestSettingPageA.cs | 32 +---- .../TestSettingPageB.cs | 32 +---- .../WebEndpoint/EndpointManager.cs | 2 + .../WebEvent/EventManager.cs | 4 +- .../WebFragment/FragmentManager.cs | 4 +- .../WebIdentity/IdentityManager.cs | 9 +- src/WebExpress.WebCore/WebJob/JobManager.cs | 29 +---- src/WebExpress.WebCore/WebLog/LogManager.cs | 2 + .../WebPackage/PackageManager.cs | 4 +- src/WebExpress.WebCore/WebPage/IPage.cs | 6 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 19 +-- .../WebPlugin/PluginManager.cs | 9 +- .../WebResource/ResourceManager.cs | 21 +--- .../WebRestAPI/RestApiManager.cs | 21 +--- .../WebSession/SessionManager.cs | 17 +-- .../WebSettingPage/ISettingPage.cs | 24 ++-- .../WebSettingPage/SettingPageManager.cs | 109 +++++++++++------- .../WebSitemap/SitemapManager.cs | 13 ++- .../WebStatusPage/StatusPageManager.cs | 4 +- src/WebExpress.WebCore/WebTask/TaskManager.cs | 2 + 20 files changed, 149 insertions(+), 214 deletions(-) diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/TestSettingPageA.cs index 8ada1d4..f79a101 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageA.cs @@ -10,13 +10,8 @@ namespace WebExpress.WebCore.Test [Title("webindex:settingpagea.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] - public sealed class TestSettingPageA : ISettingPage + public sealed class TestSettingPageA : ISettingPage { - /// - /// Returns or sets the title of the setting page. - /// - public string Title { get; set; } - /// /// Returns or sets the setting page context. /// @@ -37,33 +32,18 @@ public TestSettingPageA(ISettingPageContext pageContext) } } - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { - - } - /// /// Processing of the page. /// - /// The context for rendering the setting page. - public void Process(IRenderContext context) + /// The context for rendering the setting page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the context - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } } } diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/TestSettingPageB.cs index e07954e..3375812 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageB.cs @@ -10,13 +10,8 @@ namespace WebExpress.WebCore.Test [Title("webindex:settingpageb.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] - public sealed class TestSettingPageB : ISettingPage + public sealed class TestSettingPageB : ISettingPage { - /// - /// Returns or sets the title of the setting page. - /// - public string Title { get; set; } - /// /// Returns or sets the setting page context. /// @@ -37,33 +32,18 @@ public TestSettingPageB(ISettingPageContext pageContext) } } - /// - /// Redirects to the specified URI. - /// - /// The URI to redirect to. - public void Redirecting(string uri) - { - - } - /// /// Processing of the page. /// - /// The context for rendering the setting page. - public void Process(IRenderContext context) + /// The context for rendering the setting page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) { // test the context - if (context == null) + if (renderContext == null) { - throw new ArgumentNullException(nameof(context), "Parameter cannot be null or empty."); + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); } } - - /// - /// Release of unmanaged resources reserved during use. - /// - public void Dispose() - { - } } } diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 0623992..a5ff0a5 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -37,6 +38,7 @@ public sealed class EndpointManager : IEndpointManager, ISystemComponent /// /// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index b0eb459..b0a6d95 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -44,7 +45,8 @@ public sealed class EventManager : IEventManager, ISystemComponent /// /// The component hub. /// The reference to the context of the host. - internal EventManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] + private EventManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 14f3bfb..1efe7cb 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -50,6 +51,7 @@ public sealed class FragmentManager : IFragmentManager ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private FragmentManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -409,7 +411,7 @@ public IEnumerable GetFragments(IApplication /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope) { - scope = scope ?? typeof(IScope); + scope ??= typeof(IScope); return _dictionary.Values .SelectMany(x => x) diff --git a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs index d981692..9b2dea0 100644 --- a/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs +++ b/src/WebExpress.WebCore/WebIdentity/IdentityManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using System.Security; @@ -23,8 +24,8 @@ public class IdentityManager : IIdentityManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly IdentityPermissionDictionary _permissionDictionary = new(); - private readonly IdentityRoleDictionary _roleDictionary = new(); + private readonly IdentityPermissionDictionary _permissionDictionary = []; + private readonly IdentityRoleDictionary _roleDictionary = []; /// /// Returns all permissions. @@ -57,6 +58,7 @@ public class IdentityManager : IIdentityManager /// /// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private IdentityManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -112,7 +114,7 @@ private void Register(IApplicationContext applicationContext) /// Registers roles and ientities for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -579,6 +581,7 @@ public static string ComputeHash(SecureString input) ///
public void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebJob/JobManager.cs b/src/WebExpress.WebCore/WebJob/JobManager.cs index 0ccebd5..a6fe07c 100644 --- a/src/WebExpress.WebCore/WebJob/JobManager.cs +++ b/src/WebExpress.WebCore/WebJob/JobManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -49,6 +50,7 @@ public sealed class JobManager : IJobManager, ISystemComponent, IExecutableEleme ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private JobManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -68,31 +70,6 @@ private JobManager(IComponentHub componentHub, IHttpServerContext httpServerCont ); } - /// - /// Registers a dynamic job. - /// - /// The plugin context. - /// The cropn-object. - /// The job. - private IJob Register(IPluginContext pluginContext, Cron cron) where T : IJob - { - // create context - var jobContext = new JobContext() - { - PluginContext = pluginContext, - JobId = new ComponentId(typeof(T).FullName), - Cron = cron - }; - - var item = new ScheduleItem(_componentHub, _httpServerContext, pluginContext, null, jobContext, typeof(T)); - - _ = _dynamicScheduleList.Append(item); - - OnAddJob(jobContext); - - return item.Instance; - } - /// /// Discovers and binds static jobs to an application. /// @@ -128,7 +105,7 @@ private void Register(IApplicationContext applicationContext) /// Registers resources for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; diff --git a/src/WebExpress.WebCore/WebLog/LogManager.cs b/src/WebExpress.WebCore/WebLog/LogManager.cs index 58b9a70..7550f36 100644 --- a/src/WebExpress.WebCore/WebLog/LogManager.cs +++ b/src/WebExpress.WebCore/WebLog/LogManager.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPlugin; @@ -34,6 +35,7 @@ public class LogManager : ILogManager, ISystemComponent ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private LogManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index 133cee6..d7f7cda 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.IO.Compression; using System.Linq; @@ -53,6 +54,7 @@ public sealed class PackageManager : IPackageManager, ISystemComponent /// The component hub. /// The plugin manager. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private PackageManager(IComponentHub componentHub, IPluginManager pluginManager, IHttpServerContext httpServerContext) { _componentHub = componentHub as ComponentHub; @@ -470,7 +472,7 @@ private void OnRemovePackage(PackageCatalogItem item) ///
private void Log() { - if (!Catalog.Packages.Any()) + if (Catalog.Packages.Count == 0) { return; } diff --git a/src/WebExpress.WebCore/WebPage/IPage.cs b/src/WebExpress.WebCore/WebPage/IPage.cs index 5a282c9..f5eb23e 100644 --- a/src/WebExpress.WebCore/WebPage/IPage.cs +++ b/src/WebExpress.WebCore/WebPage/IPage.cs @@ -12,14 +12,14 @@ public interface IPage : IPage /// /// Defines the contract for a page resource that can be rendered using a specific context. /// - /// The type of the visual tree. - public interface IPage : IEndpoint where T : IVisualTree + /// The type of the visual tree. + public interface IPage : IEndpoint where TVisualTree : IVisualTree { /// /// Processing of the page. /// /// The context for rendering the page. /// The visual tree to be rendered. - void Process(IRenderContext renderContext, T visualTree); + void Process(IRenderContext renderContext, TVisualTree visualTree); } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index b582def..a18ff96 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -50,6 +51,7 @@ public class PageManager : IPageManager ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private PageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -486,23 +488,6 @@ internal void Remove(IApplicationContext applicationContext) } } - /// - /// Returns an enumeration of all containing page items of a plugin. - /// - /// A context of a plugin whose pages are to be registered. - /// An enumeration of pages items. - private IEnumerable GetPageItems(IPluginContext pluginContext) - { - if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x.Value); - } - - return []; - } - /// /// Raises the AddPage event. /// diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 5f710f3..686ff8f 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -44,6 +45,7 @@ public sealed class PluginManager : IPluginManager, IExecutableElements, ISystem ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private PluginManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -92,8 +94,7 @@ internal void Register() } // register plugin - foreach (var assembly in assemblies - .OrderBy(x => x.GetCustomAttribute(typeof(SystemPluginAttribute)) != null ? 0 : 1)) + foreach (var assembly in assemblies.OrderBy(x => x.GetCustomAttribute() != null ? 0 : 1)) { Register(assembly); } @@ -158,7 +159,7 @@ internal IEnumerable Register(string pluginFile) ///
/// The assembly where the plugin is located. /// The plugin load context for isolating and unloading the dependent libraries. - /// A plugin created or null. + /// A collection of created plugin contexts. private IEnumerable Register(Assembly assembly, PluginLoadContext loadContext = null) { var plugins = new List(); @@ -654,7 +655,7 @@ private void Log() .Where ( x => x.Value.PluginClass.Assembly - .GetCustomAttribute(typeof(SystemPluginAttribute)) == null + .GetCustomAttribute() == null ) .Select(x => I18N.Translate ( diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index fffdbb5..06f3fd7 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -46,6 +47,7 @@ public sealed class ResourceManager : IResourceManager, ISystemComponent ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private ResourceManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -116,7 +118,7 @@ private void Register(IApplicationContext applicationContext) /// Registers resources for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -251,23 +253,6 @@ internal void Remove(IApplicationContext applicationContext) } } - /// - /// Returns an enumeration of all containing resource items of a plugin. - /// - /// A context of a plugin whose resources are to be registered. - /// An enumeration of resource items. - private IEnumerable GetResorceItems(IPluginContext pluginContext) - { - if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x.Value); - } - - return []; - } - /// /// Returns an enumeration of all containing resource contexts of a plugin. /// diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index c2b9c8a..3042159 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Text.Json; @@ -49,6 +50,7 @@ public class RestApiManager : IRestApiManager ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -289,7 +291,7 @@ private void Register(IApplicationContext applicationContext) /// Registers rest apis for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; @@ -452,23 +454,6 @@ internal void Remove(IApplicationContext applicationContext) } } - /// - /// Returns an enumeration of all containing rest api resource items of a plugin. - /// - /// A context of a plugin whose rest api resources are to be registered. - /// An enumeration of rest api resource items. - private IEnumerable GetRestApiItems(IPluginContext pluginContext) - { - if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x.Value); - } - - return []; - } - /// /// Raises the AddRestApi event. /// diff --git a/src/WebExpress.WebCore/WebSession/SessionManager.cs b/src/WebExpress.WebCore/WebSession/SessionManager.cs index b839be1..b2108f2 100644 --- a/src/WebExpress.WebCore/WebSession/SessionManager.cs +++ b/src/WebExpress.WebCore/WebSession/SessionManager.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; @@ -19,7 +20,8 @@ public class SessionManager : ISessionManager, ISystemComponent /// Initializes a new instance of the class. ///
/// The reference to the context of the host. - internal SessionManager(IHttpServerContext context) + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] + private SessionManager(IHttpServerContext context) { _httpServerContext = context; @@ -43,30 +45,30 @@ public Session GetSession(Request request) .Cookies?.Where(x => x.Name.Equals("session", StringComparison.OrdinalIgnoreCase)) .FirstOrDefault(); - Guid Guid = Guid.NewGuid(); + var guid = Guid.NewGuid(); try { - Guid = Guid.Parse(sessionCookie?.Value); + guid = Guid.Parse(sessionCookie?.Value); } catch { } - if (sessionCookie != null && _dictionary.ContainsKey(Guid)) + if (sessionCookie != null && _dictionary.TryGetValue(guid, out Session value)) { - session = _dictionary[Guid]; + session = value; session.Updated = DateTime.Now; } else { // no or invalid session => assign new session - session = new Session(Guid); + session = new Session(guid); lock (_dictionary) { - _dictionary[Guid] = session; + _dictionary[guid] = session; } } @@ -78,6 +80,7 @@ public Session GetSession(Request request) ///
public void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs index 3bb4765..3bc3b85 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPage.cs @@ -6,27 +6,21 @@ namespace WebExpress.WebCore.WebSettingPage /// /// Defines the contract for a setting page resource. /// - public interface ISettingPage : IEndpoint + public interface ISettingPage : ISettingPage { - /// - /// Processing of the page. - /// - /// The context for rendering the page. - void Process(IRenderContext context); - - /// - /// Redirect to another page. - /// The function throws the RedirectException. - /// - /// The uri to redirect to. - void Redirecting(string uri); } /// /// Defines the contract for a setting page resource that can be rendered using a specific context. /// - /// The type of the render context. - public interface ISettingPage : ISettingPage where T : IRenderContext + /// The type of the visual tree. + public interface ISettingPage : IEndpoint where TVisualTree : IVisualTree { + /// + /// Processing of the page. + /// + /// The context for rendering the setting page. + /// The visual tree to be rendered. + void Process(IRenderContext renderContext, TVisualTree visualTree); } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 15a8183..90ce262 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; +using System.Reflection; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; @@ -22,6 +25,7 @@ public sealed class SettingPageManager : ISettingPageManager private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly SettingPageDictionary _dictionary = []; + private static readonly Dictionary _delegateCache = []; /// /// An event that fires when an setting page is added. @@ -43,6 +47,7 @@ public sealed class SettingPageManager : ISettingPageManager /// /// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -59,28 +64,76 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe EndpointsResolver = () => SettingPages, HandleRequest = (request, endpontContext) => { - var settingPage = CreateSettingPageInstance(endpontContext as ISettingPageContext); - var settingPageType = settingPage.GetType(); - var context = default(IRenderContext); - var pageContetx = endpontContext as IPageContext; - - if (settingPageType.IsGenericType) + var pageInstance = CreateSettingPageInstance(endpontContext as ISettingPageContext); + var pageType = pageInstance.GetType(); + var pageContext = endpontContext as IPageContext; + var renderContext = new RenderContext(pageInstance, pageContext, request); + var visualTreeContext = new VisualTreeContext(renderContext); + + var visualTreeType = pageType.GetInterface(typeof(IPage<>).Name).GetGenericArguments()[0]; + if (!_delegateCache.TryGetValue(pageType, out var del)) { - var typeOfT = settingPageType.GetGenericArguments()[0]; - var parameters = new object[] { settingPage, endpontContext as IPageContext, request }; + // create and compile the expression + var renderContextParam = Expression.Parameter(typeof(IRenderContext), "renderContext"); + var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var processMethod = pageType.GetMethod("Process", [typeof(IRenderContext), visualTreeType]); + var callProzessMethod = Expression.Call + ( + Expression.Constant(pageInstance), + processMethod, + renderContextParam, + visualTreeParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) + .Compile(); - context = Activator.CreateInstance(typeOfT, parameters) as IRenderContext; + _delegateCache[pageType] = lambda; + del = lambda; + } + + // create visual tree instance + var visualTreeInstance = default(IVisualTree); + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + var constructors = visualTreeType?.GetConstructors(flags); + + if (constructors != null) + { + foreach (var constructor in constructors.OrderByDescending(x => x.GetParameters().Length)) + { + // injection + var parameters = constructor.GetParameters(); + var hubProperties = _componentHub.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + var contextIdProperty = pageContext.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(x => x.PropertyType == typeof(IComponentId)) + .FirstOrDefault(); + + var parameterValues = parameters.Select(parameter => + parameter.ParameterType == typeof(IComponentHub) ? componentHub : + parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : + parameter.ParameterType == typeof(IPageContext) ? pageContext : + parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(pageContext) : + hubProperties.Where(x => x.PropertyType == parameter.ParameterType) + .FirstOrDefault()? + .GetValue(componentHub) ?? null + ).ToArray(); + + if (constructor.Invoke(parameterValues) is IVisualTree visualTree) + { + visualTreeInstance = visualTree; + } + } } else { - context = new RenderContext(settingPage, pageContetx, request); + visualTreeInstance = Activator.CreateInstance(); } - settingPage.Process(context); + // execute the cached delegate + del.DynamicInvoke(renderContext, visualTreeInstance); return new ResponseOK() { - //Content = context.VisualTree.Render(new VisualTreeContext(context)) + Content = visualTreeInstance.Render(visualTreeContext) }; } }; @@ -158,13 +211,14 @@ private void Register(IApplicationContext applicationContext) /// Registers pages for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext.Assembly; foreach (var settingPageType in assembly.GetTypes() - .Where(x => x.IsClass && x.IsSealed && (x.GetInterfaces().Contains(typeof(ISettingPage))))) + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(ISettingPage<>).Name) != null)) { var id = settingPageType.FullName?.ToLower(); var title = settingPageType.Name; @@ -283,9 +337,6 @@ private void Register(IPluginContext pluginContext, IEnumerable @@ -421,30 +472,6 @@ private void OnRemoveApplication(object sender, IApplicationContext e) Remove(e); } - /// - /// Information about the component is collected and prepared for output in the log. - /// - private void Log() - { - //output.Add - //( - // string.Empty.PadRight(deep) + - // I18N.Translate("webexpress.webui:settingpagemanager.titel") - //); - - //var log = new List - // { - // I18N.Translate("webexpress.webapp:pagesettingmanager.register"), - // " SettingContext = " + context ?? "null", - // " SettingSection = " + section.ToString(), - // " SettingGroup = " + group ?? "null", - // " SettingPage.Id = " + page?.Id ?? "null", - // " SettingPage.Hide = " + (page?.Hide != null ? page?.Hide.ToString() : "null") - // }; - - //_httpServerContext.Log.Debug(string.Join(Environment.NewLine, log)); - } - /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 37a7a3e..a871c59 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -33,6 +34,7 @@ public sealed class SitemapManager : ISitemapManager, ISystemComponent ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private SitemapManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -129,7 +131,7 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) /// /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// /// Returns the uri taking into account the context or null. public UriResource GetUri(params Parameter[] parameters) where T : IEndpoint @@ -163,7 +165,7 @@ public UriResource GetUri(Type resourceType, params Parameter[] parameters) /// /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// The application context. /// Returns the uri taking into account the context or null. public UriResource GetUri(IApplicationContext applicationContext) where T : IEndpoint @@ -180,12 +182,12 @@ public UriResource GetUri(IApplicationContext applicationContext) where T : I /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). /// The endpoint context. /// Returns the uri taking into account the context or null. - public UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint + public UriResource GetUri(IEndpointContext endpointContext) where TEnpoint : IEndpoint { - var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T), endpointContext.ApplicationContext) + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(TEnpoint), endpointContext.ApplicationContext) .Where(x => x.EndpointId.Equals(endpointContext.EndpointId)); var node = _root.GetPreOrder() @@ -202,7 +204,6 @@ public UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoi ///
/// The path segments of the context path. /// The application context. - /// The parent node or null if root. /// The sitemap root node. private static SitemapNode CreateSiteMap ( diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index e348a71..d114d7f 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -51,6 +52,7 @@ public class StatusPageManager : IStatusPageManager, ISystemComponent ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private StatusPageManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -103,7 +105,7 @@ private void Register(IApplicationContext applicationContext) /// Registers resources for a given plugin and application context. ///
/// The plugin context. - /// The application context (optional). + /// The application context (optional). private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext?.Assembly; diff --git a/src/WebExpress.WebCore/WebTask/TaskManager.cs b/src/WebExpress.WebCore/WebTask/TaskManager.cs index 29032db..86f38b4 100644 --- a/src/WebExpress.WebCore/WebTask/TaskManager.cs +++ b/src/WebExpress.WebCore/WebTask/TaskManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebTask.Model; @@ -25,6 +26,7 @@ public class TaskManager : ITaskManager, ISystemComponent ///
/// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private TaskManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; From 05ee19026c9b028e6869c1527f1b39195e8f57cd Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 20:21:51 +0100 Subject: [PATCH 119/162] chore: general code improvements --- .../Manager/UnitTestSitemapManager.cs | 1 - .../WebHtml/HtmlElementMetadataHead.cs | 133 +++++++----------- 2 files changed, 47 insertions(+), 87 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 3005a14..3c80e3d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -119,5 +119,4 @@ public void IsIComponentManager() Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.SitemapManager.GetType())); } } - } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs index b5a8905..c9341f3 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMetadataHead.cs @@ -10,156 +10,117 @@ namespace WebExpress.WebCore.WebHtml ///
public class HtmlElementMetadataHead : HtmlElement, IHtmlElementMetadata { + private readonly HtmlElementMetadataTitle _elementTitle = new HtmlElementMetadataTitle(); + private readonly HtmlElementMetadataBase _elementBase = new HtmlElementMetadataBase(); + private IEnumerable _elementFavicons = []; + private IEnumerable _elementStyles = []; + private IEnumerable _elementScripts = []; + private IEnumerable _elementScriptLinks = []; + private IEnumerable _elementCssLinks = []; + private IEnumerable _elementMeta = []; + /// /// Returns or sets the title. /// public string Title { - get => ElementTitle.Title; - set => ElementTitle.Title = value; + get => _elementTitle.Title; + set => _elementTitle.Title = value; } - /// - /// Returns or sets the title element. - /// - private HtmlElementMetadataTitle ElementTitle { get; set; } - /// /// Returns or sets the base. /// public string Base { - get => ElementBase.Href; - set => ElementBase.Href = value; + get => _elementBase.Href; + set => _elementBase.Href = value; } - /// - /// Returns or sets the element base. - /// - private HtmlElementMetadataBase ElementBase { get; set; } - /// /// Returns or sets the favicon. /// public IEnumerable Favicons { - get => (from x in ElementFavicons select new Favicon(x.Href, x.Type)).ToList(); + get => _elementFavicons.Select(x => new Favicon(x.Href, x.Type)); set { - ElementFavicons.Clear(); - ElementFavicons.AddRange - ( - from x in value - select new HtmlElementMetadataLink() - { - Href = x.Url, - Rel = "icon", - Type = x.Mediatype != TypeFavicon.Default ? x.GetMediatyp() : "" - }); + _elementFavicons = value.Select(x => new HtmlElementMetadataLink() + { + Href = x.Url, + Rel = "icon", + Type = x.Mediatype != TypeFavicon.Default ? x.GetMediatyp() : "" + }); } } - /// - /// Returns or sets the favicon link. - /// - private List ElementFavicons { get; set; } - /// /// Returns or sets the internal stylesheet. /// public IEnumerable Styles { - get => (from x in ElementStyles select x.Code).ToList(); - set { ElementStyles.Clear(); ElementStyles.AddRange(from x in value select new HtmlElementMetadataStyle(x)); } + get => _elementStyles.Select(x => x.Code); + set { _elementStyles = value.Select(x => new HtmlElementMetadataStyle(x)); } } - /// - /// Returns or sets the style elements. - /// - private List ElementStyles { get; set; } - /// /// Returns or sets the scripts. /// public IEnumerable Scripts { - get => (from x in ElementScripts select x.Code).ToList(); - set { ElementScripts.Clear(); ElementScripts.AddRange(from x in value select new HtmlElementScriptingScript(x)); } + get => _elementScriptLinks.Select(x => x.Code); + set { _elementScripts = value.Select(x => new HtmlElementScriptingScript(x)); } } - /// - /// Returns or sets the script elements. - /// - private List ElementScripts { get; set; } - /// /// Returns or sets the text/javascript. /// public IEnumerable ScriptLinks { - get => (from x in ElementScriptLinks select x.Src).ToList(); + get => _elementScriptLinks.Select(x => x.Src); set { - ElementScriptLinks.Clear(); ElementScriptLinks.AddRange(from x in value - select new HtmlElementScriptingScript() { Language = "javascript", Src = x, Type = "text/javascript" }); + _elementScriptLinks = value.Select(x => new HtmlElementScriptingScript() + { + Language = "javascript", + Src = x, + Type = "text/javascript" + }); } } - /// - /// Returns or sets the external scripts. - /// - private List ElementScriptLinks { get; set; } - /// /// Returns or sets the internal stylesheet. /// public IEnumerable CssLinks { - get => (from x in ElementCssLinks select x.Href).ToList(); + get => _elementCssLinks.Select(x => x.Href); set { - ElementCssLinks.Clear(); ElementCssLinks.AddRange(from x in value - select new HtmlElementMetadataLink() { Rel = "stylesheet", Href = x, Type = "text/css" }); + _elementCssLinks = value.Select(x => new HtmlElementMetadataLink() + { + Rel = "stylesheet", + Href = x, + Type = "text/css" + }); } } - /// - /// Returns or sets the css link. - /// - private List ElementCssLinks { get; set; } - /// /// Returns or sets the metadata. /// public IEnumerable> Meta { - get => (from x in ElementMeta select new KeyValuePair(x.Key, x.Value)).ToList(); - set - { - ElementMeta.Clear(); ElementMeta.AddRange(from x in value - select new HtmlElementMetadataMeta(x.Key, x.Value)); - } + get => _elementMeta.Select(x => new KeyValuePair(x.Key, x.Value)); + set { _elementMeta = value.Select(x => new HtmlElementMetadataMeta(x.Key, x.Value)); } } - /// - /// Returns or sets the metadata elements. - /// - private List ElementMeta { get; set; } - /// /// Initializes a new instance of the class. /// public HtmlElementMetadataHead() : base("head") { - ElementTitle = new HtmlElementMetadataTitle(); - ElementBase = new HtmlElementMetadataBase(); - ElementFavicons = new List(); - ElementStyles = new List(); - ElementScripts = new List(); - ElementScriptLinks = new List(); - ElementCssLinks = new List(); - ElementMeta = new List(); } /// @@ -173,7 +134,7 @@ public override void ToString(StringBuilder builder, int deep) if (!string.IsNullOrWhiteSpace(Title)) { - ElementTitle.ToString(builder, deep + 1); + _elementTitle.ToString(builder, deep + 1); } if (!string.IsNullOrWhiteSpace(Base)) @@ -181,32 +142,32 @@ public override void ToString(StringBuilder builder, int deep) //ElementBase.ToString(builder, deep + 1); } - foreach (var v in ElementFavicons) + foreach (var v in _elementFavicons) { v.ToString(builder, deep + 1); } - foreach (var v in ElementStyles) + foreach (var v in _elementStyles) { v.ToString(builder, deep + 1); } - foreach (var v in ElementScriptLinks) + foreach (var v in _elementScriptLinks) { v.ToString(builder, deep + 1); } - foreach (var v in ElementScripts) + foreach (var v in _elementScripts) { v.ToString(builder, deep + 1); } - foreach (var v in ElementCssLinks) + foreach (var v in _elementCssLinks) { v.ToString(builder, deep + 1); } - foreach (var v in ElementMeta) + foreach (var v in _elementMeta) { v.ToString(builder, deep + 1); } From 2691ae6c390a5e0e46ad0a53c080f6d2ba942f1f Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 29 Dec 2024 21:31:38 +0100 Subject: [PATCH 120/162] fix: bug fixes and refactoring --- .../Manager/UnitTestSettingPageManager.cs | 3 +- .../Manager/UnitTestSitemapManager.cs | 2 +- .../WebComponent/ComponentActivator.cs | 14 +-- .../WebEndpoint/EndpointManager.cs | 12 +-- .../WebSettingPage/ISettingPageContext.cs | 17 +--- .../WebSettingPage/Model/SettingPageItem.cs | 4 +- .../WebSettingPage/SettingPageContext.cs | 89 +------------------ .../WebSettingPage/SettingPageManager.cs | 18 ++-- 8 files changed, 32 insertions(+), 127 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 6cf868e..d899fec 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -71,7 +71,6 @@ public void Id(Type applicationType, Type resourceType, string id) [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "webindex:settingpageb.label")] [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webindex:settingpagea.label")] [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webindex:settingpageb.label")] - public void Title(Type applicationType, Type resourceType, string title) { // preconditions @@ -80,7 +79,7 @@ public void Title(Type applicationType, Type resourceType, string title) var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(title, settingPage.SettingPageTitle); + Assert.Equal(title, settingPage.PageTitle); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 3c80e3d..85c9dd4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -22,7 +22,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(43, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(46, componentManager.SitemapManager.SiteMap.Count()); } /// diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index c976dab..ff33f76 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -221,15 +221,17 @@ public static T CreateInstance(IHttpServerContext httpServerContext, ICompone /// /// Creates an instance of the specified component type with the provided context and component hub and advanced parameters. /// - /// The type of the component, which must implement . - /// The type of the context, which must implement . + /// The type of the component, which must implement . + /// The type of the context, which must implement . /// The type of the component to create. /// The context to pass to the component's constructor. /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static T CreateInstance(Type componentType, C context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where T : class, IComponent where C : IContext + public static TComponent CreateInstance(Type componentType, TContext context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) + where TComponent : class, IComponent + where TContext : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -248,7 +250,7 @@ public static T CreateInstance(Type componentType, C context, IHttpServerC var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : - parameter.ParameterType == typeof(C) ? context : + parameter.ParameterType == typeof(TContext) ? context : parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(context) : hubProperties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? @@ -257,14 +259,14 @@ public static T CreateInstance(Type componentType, C context, IHttpServerC .FirstOrDefault() ?? null ).ToArray(); - if (constructor.Invoke(parameterValues) is T component) + if (constructor.Invoke(parameterValues) is TComponent component) { return component; } } } - return Activator.CreateInstance(componentType) as T; + return Activator.CreateInstance(componentType) as TComponent; } /// diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index a5ff0a5..ca85b7e 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -54,11 +54,11 @@ private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServe /// /// Registers an endpoint context type. /// - /// The type of the endpoint context. + /// The type of the endpoint context. /// The registration details containing the callback functions. - public void Register(EndpointRegistration endpointRegistration) where T : IEndpointContext + public void Register(EndpointRegistration endpointRegistration) where TEndpointContext : IEndpointContext { - var type = typeof(T); + var type = typeof(TEndpointContext); if (!_registrations.ContainsKey(type)) { _registrations[type] = endpointRegistration; @@ -71,10 +71,10 @@ public void Register(EndpointRegistration endpointRegistration) where T : IEn /// /// Removes the registration for a specific endpoint context type. /// - /// The type of the endpoint context. - public void Remove() where T : IEndpointContext + /// The type of the endpoint context. + public void Remove() where TEndpointContext : IEndpointContext { - var type = typeof(T); + var type = typeof(TEndpointContext); _registrations.Remove(type, out var endpointRegistration); endpointRegistration.AddEndpoint -= OnAddEndpoint; diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index 96f6177..8f8a8c5 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage @@ -8,20 +7,8 @@ namespace WebExpress.WebCore.WebSettingPage /// Interface representing the context of a setting page. /// Provides access to plugin context, application context, conditions for activation, and caching behavior. /// - public interface ISettingPageContext : IEndpointContext + public interface ISettingPageContext : IPageContext { - /// - /// Returns the setting page title. - /// - string SettingPageTitle { get; } - - /// - /// Returns the scope names that provides the setting page. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - IEnumerable Scopes { get; } - /// /// Returns the group to which the setting page belongs. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index 63c5b03..cf44b95 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -1,6 +1,6 @@ using System; -using WebExpress.WebCore.WebSettingPage; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebSettingPage.Model @@ -33,7 +33,7 @@ public class SettingPageItem : IDisposable /// /// Returns or sets the instance of the setting page, if the page is cached, otherwise null. /// - public ISettingPage Instance { get; internal set; } + public IEndpoint Instance { get; internal set; } /// /// Returns the setting context. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 6d0d112..27e1ac2 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -1,11 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; -using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage.Model; using WebExpress.WebCore.WebUri; @@ -15,55 +10,13 @@ namespace WebExpress.WebCore.WebSettingPage /// Interface representing the context of a setting page. /// Provides access to plugin context, application context, conditions for activation, and caching behavior. /// - public class SettingPageContext : ISettingPageContext + public class SettingPageContext : PageContext, ISettingPageContext { - private readonly IEndpointManager _endpointManager; - private readonly Type _parentType; - private readonly UriResource _contextPath; - private readonly IUriPathSegment _pathSegment; - - /// - /// Returns the context of the associated plugin. - /// - public IPluginContext PluginContext { get; internal set; } - - /// - /// Returns the application context. - /// - public IApplicationContext ApplicationContext { get; internal set; } - - /// - /// Returns the unique identifier for the setting page. - /// - public IComponentId EndpointId { get; internal set; } - - /// - /// Returns the setting page title. - /// - public string SettingPageTitle { get; internal set; } - - /// - /// Returns the scope names that provides the setting page. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - public IEnumerable Scopes { get; internal set; } = []; - /// /// Returns the group to which the setting page belongs. /// public string Group { get; internal set; } - /// - /// Returns the conditions that must be met for the component to be active. - /// - public IEnumerable Conditions { get; internal set; } - - /// - /// Returns a value indicating whether the component is created once and reused on each execution. - /// - public bool Cache { get; internal set; } - /// /// Returns a value indicating whether the page should be displayed or hidden. /// @@ -84,39 +37,6 @@ public class SettingPageContext : ISettingPageContext /// public SettingSection Section { get; internal set; } - /// - /// Returns the parent context of the endpoint. - /// - public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) - .FirstOrDefault(); - - /// - /// Returns a value indicating whether to include sub-paths. - /// - public bool IncludeSubPaths { get; internal set; } - - /// - /// Returns the context path. - /// - public UriResource ContextPath - { - get - { - var parentContext = ParentContext; - if (parentContext != null) - { - return UriResource.Combine(ParentContext?.Uri, _contextPath); - } - - return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); - } - } - - /// - /// Returns the URI of the setting page. - /// - public UriResource Uri => ContextPath.Append(_pathSegment); - /// /// Initializes a new instance of the class with the specified parent type and context path. /// @@ -125,11 +45,8 @@ public UriResource ContextPath /// The context path of the resource. /// The path segment of the resource. public SettingPageContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) + : base(endpointManager, parentType, contextPath, pathSegment) { - _endpointManager = endpointManager; - _parentType = parentType; - _contextPath = contextPath; - _pathSegment = pathSegment; } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 90ce262..1a009f8 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -70,7 +70,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe var renderContext = new RenderContext(pageInstance, pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); - var visualTreeType = pageType.GetInterface(typeof(IPage<>).Name).GetGenericArguments()[0]; + var visualTreeType = pageType.GetInterface(typeof(ISettingPage<>).Name).GetGenericArguments()[0]; if (!_delegateCache.TryGetValue(pageType, out var del)) { // create and compile the expression @@ -141,7 +141,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe AddSettingPage += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); RemoveSettingPage += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); - _componentHub.EndpointManager.Register(endpointtRegistration); + _componentHub.EndpointManager.Register(endpointtRegistration); _httpServerContext.Log.Debug(I18N.Translate("webexpress.webapp:pagesettingmanager.initialization")); } @@ -149,9 +149,9 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe /// /// Creates a new setting page and returns it. If a page already exists (through caching), the existing instance is returned. /// - /// The context used for setting page creation. + /// The context used for setting page creation. /// The created or cached page. - private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageContext) + private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContext) { var settingPageItem = _dictionary.Values .SelectMany(a => a.Values) @@ -159,11 +159,11 @@ private ISettingPage CreateSettingPageInstance(ISettingPageContext settinPageCon .SelectMany(s => s.Values) .SelectMany(g => g.Values) .SelectMany(i => i) - .FirstOrDefault(x => x.SettingPageContext.Equals(settinPageContext)); + .FirstOrDefault(x => x.SettingPageContext.Equals(settingPageContext)); if (settingPageItem != null && settingPageItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(settingPageItem.SettingPageClass, settinPageContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance(settingPageItem.SettingPageClass, settingPageContext, _httpServerContext, _componentHub); if (settingPageItem.Cache) { @@ -225,7 +225,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); + var scopes = new List(); var context = default(string); var group = default(string); var section = SettingSection.Primary; @@ -284,7 +284,7 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ScopeAttribute<>).Namespace) { - scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()?.FullName?.ToLower()); + scopes.Add(customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault()); } } @@ -296,7 +296,7 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Fri, 3 Jan 2025 10:34:24 +0100 Subject: [PATCH 121/162] fix: add elements to bulleted list --- .../WebSitemap/ISitemapManager.cs | 7 +++++++ .../WebSitemap/SitemapManager.cs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs index c65a63b..863ec2e 100644 --- a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -62,5 +62,12 @@ public interface ISitemapManager : IComponentManager /// The endpoint context. /// Returns the uri taking into account the context or null. UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint; + + /// + /// Retrieves the endpoint context associated with the given URI. + /// + /// The URI resource to search for. + /// The endpoint context if found, otherwise null. + IEndpointContext GetEndpoint(UriResource uri); } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index a871c59..5aafb47 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -197,6 +197,24 @@ public UriResource GetUri(IEndpointContext endpointContext) where TEnp return node?.EndpointContext?.Uri; } + /// + /// Retrieves the endpoint context associated with the given URI. + /// + /// The URI resource to search for. + /// The endpoint context if found, otherwise null. + public IEndpointContext GetEndpoint(UriResource uri) + { + var variables = new Dictionary(); + var result = SearchNode + ( + _root, + new Queue(uri.PathSegments.Select(x => x.ToString())), + new Queue(), + new SearchContext() + ); + return result?.EndpointContext; + } + /// /// Creates the sitemap. Works recursively. /// It is important for the algorithm that the addition of application is sorted From ec01d5733abae09672a5fd8897ed80b54f06aa45 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 3 Jan 2025 10:35:01 +0100 Subject: [PATCH 122/162] bug fixes --- .../Manager/UnitTestSitemapManager.cs | 53 ++++++++++++++++++- .../WebHtml/HtmlElementTextContentOl.cs | 11 ++-- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 85c9dd4..c7df0eb 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,6 +1,7 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSitemap; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Manager { @@ -26,7 +27,7 @@ public void Refresh() } /// - /// Test the SearchResource function of the sitemap. + /// Test the search resource function of the sitemap. /// [Theory] [InlineData("http://localhost:8080/server/appa/resa", "webexpress.webcore.test.testresourcea")] @@ -84,7 +85,7 @@ public void SearchResource(string uri, string id) } /// - /// Test the GetUri function of the sitemap. + /// Test the get uri function of the sitemap. /// [Theory] [InlineData(typeof(TestResourceA), "/server/appa/resa")] @@ -106,6 +107,54 @@ public void GetUri(Type resourceType, string expected) Assert.Equal(expected, uri?.ToString()); } + /// + /// Test the get endpoint function of the sitemap. + /// + [Theory] + [InlineData("http://localhost:8080/server/appa/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/appa/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/appa/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/appa/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/appb/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/appb/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/appb/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/appb/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/resa", "webexpress.webcore.test.testresourcea")] + [InlineData("http://localhost:8080/server/resa/resb", "webexpress.webcore.test.testresourceb")] + [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] + [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] + [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/appa/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/appb/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] + [InlineData("http://localhost:8080/server/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] + [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] + [InlineData("http://localhost:8080/server/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] + [InlineData("http://localhost:8080/server/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] + [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/css.mycss.css", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] + [InlineData("http://localhost:8080/uri/does/not/exist", null)] + public void GetEndpoint(string uri, string expected) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + componentHub.SitemapManager.Refresh(); + + // test execution + var endpoint = componentHub.SitemapManager.GetEndpoint(new UriResource(uri)); + + Assert.Equal(expected, endpoint?.EndpointId?.ToString()); + } + /// /// Tests whether the sitemap manager implements interface IComponentManager. /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs index 981987f..bc0106e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentOl.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Text; namespace WebExpress.WebCore.WebHtml @@ -12,7 +13,9 @@ public class HtmlElementTextContentOl : HtmlElement, IHtmlElementTextContent /// /// Returns the elements. /// - public new List Elements { get; set; } + public new IEnumerable Elements => base.Elements + .Where(x => x is HtmlElementTextContentLi) + .Select(x => x as HtmlElementTextContentLi); /// /// Initializes a new instance of the class. @@ -20,7 +23,6 @@ public class HtmlElementTextContentOl : HtmlElement, IHtmlElementTextContent public HtmlElementTextContentOl() : base("ol") { - Elements = new List(); } /// @@ -30,7 +32,7 @@ public HtmlElementTextContentOl() public HtmlElementTextContentOl(params HtmlElementTextContentLi[] nodes) : this() { - Elements.AddRange(nodes); + Add(nodes); } /// @@ -40,9 +42,6 @@ public HtmlElementTextContentOl(params HtmlElementTextContentLi[] nodes) /// The call depth. public override void ToString(StringBuilder builder, int deep) { - //base.Elements.Clear(); - //base.Elements.AddRange(Elements); - base.ToString(builder, deep); } } From ede151377ef215df9d40dc024b8628d77ff298e5 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 4 Jan 2025 15:11:01 +0100 Subject: [PATCH 123/162] changed render call parameters to support visualtree in fragments --- .../Manager/UnitTestFragmentManager.cs | 3 ++- src/WebExpress.WebCore.Test/TestFragmentA.cs | 3 ++- src/WebExpress.WebCore.Test/TestFragmentB.cs | 5 +++-- src/WebExpress.WebCore.Test/TestFragmentC.cs | 5 +++-- .../WebAttribute/AssetPathAttribute.cs | 11 ++++++++-- .../WebAttribute/CacheAttribute.cs | 2 +- .../WebAttribute/ConditionAttribute.cs | 2 +- .../WebAttribute/ContextPathAttribute.cs | 3 +++ .../WebAttribute/DataPathAttribute.cs | 8 ++++++- .../WebAttribute/DefaultAttribute.cs | 2 +- .../WebAttribute/DependencyAttribute.cs | 2 +- .../WebAttribute/DescriptionAttribute.cs | 9 +++++++- .../WebAttribute/IconAttribute.cs | 10 +++++++-- .../WebAttribute/NameAttribute.cs | 11 ++++++++-- .../WebAttribute/OrderAttribute.cs | 5 ++++- .../WebAttribute/SettingContextAttribute.cs | 2 +- .../WebAttribute/SettingSectionAttribute.cs | 2 +- .../WebFragment/FragmentManager.cs | 10 ++++++--- .../WebFragment/IFragment.cs | 9 +++++--- .../WebFragment/IFragmentManager.cs | 6 ++++- .../WebFragment/Model/FragmentDictionary.cs | 4 ++-- .../WebFragment/Model/FragmentItem.cs | 22 +++++++++++++------ .../WebSettingPage/SettingPageManager.cs | 6 +++++ 23 files changed, 105 insertions(+), 37 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 1399b34..f7e5294 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -123,9 +123,10 @@ public void Process(Type applicationType, Type sectionType, Type scopeType) var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); var renderContext = UnitTestFixture.CrerateRenderContextMock(application, [scopeType]); + var visualTree = new VisualTree(); // test execution - var html = componentHub.FragmentManager.Render(renderContext, sectionType); + var html = componentHub.FragmentManager.Render(renderContext, visualTree, sectionType); Assert.NotNull(html); Assert.NotEmpty(html.ToString()); diff --git a/src/WebExpress.WebCore.Test/TestFragmentA.cs b/src/WebExpress.WebCore.Test/TestFragmentA.cs index 4fd1ca4..0418aeb 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentA.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentA.cs @@ -46,8 +46,9 @@ public TestFragmentA(IComponentHub componentHub, IFragmentContext fragmentContex /// Processes the fragments in the specified render context. /// /// The context in which rendering occurs. + /// The visual tree used for rendering the fragment. /// An HTML node representing the rendered fragments. - public IHtmlNode Render(IRenderContext renderContext) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) { return new HtmlText("TestFragmentA"); } diff --git a/src/WebExpress.WebCore.Test/TestFragmentB.cs b/src/WebExpress.WebCore.Test/TestFragmentB.cs index 07cc2d8..99e12f6 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentB.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentB.cs @@ -12,7 +12,7 @@ namespace WebExpress.WebCore.Test [Section()] [Scope] [Order(0)] - public sealed class TestFragmentB : IFragment + public sealed class TestFragmentB : IFragment { /// /// Initialization of the fragment. Here, for example, managed resources can be loaded. @@ -38,8 +38,9 @@ public TestFragmentB(IComponentHub componentHub, IFragmentContext fragmentContex /// Processes the fragments in the specified render context. /// /// The context in which rendering occurs. + /// The visual tree used for rendering the fragment. /// An HTML node representing the rendered fragments. - public IHtmlNode Render(RenderContext renderContext) + public IHtmlNode Render(RenderContext renderContext, VisualTree visualTree) { return new HtmlText("TestFragmentB"); } diff --git a/src/WebExpress.WebCore.Test/TestFragmentC.cs b/src/WebExpress.WebCore.Test/TestFragmentC.cs index 91b26ec..4470b51 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentC.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentC.cs @@ -11,7 +11,7 @@ namespace WebExpress.WebCore.Test [Section()] [Scope] [Order(0)] - public sealed class TestFragmentC : IFragment + public sealed class TestFragmentC : IFragment { /// /// Initialization of the fragment. Here, for example, managed resources can be loaded. @@ -37,8 +37,9 @@ public TestFragmentC(IComponentHub componentHub, IFragmentContext fragmentContex /// Processes the fragments in the specified render context. /// /// The context in which rendering occurs. + /// The visual tree used for rendering the fragment. /// An HTML node representing the rendered fragments. - public IHtmlNode Render(TestRenderContext renderContext) + public IHtmlNode Render(TestRenderContext renderContext, TestVisualTree visualTree) { return new HtmlText("TestFragmentC"); } diff --git a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs index 78004ba..188e32f 100644 --- a/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/AssetPathAttribute.cs @@ -1,6 +1,13 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { - public class AssetPathAttribute : System.Attribute, IApplicationAttribute + + /// + /// Attribute to specify the path for assets in the application. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class AssetPathAttribute : Attribute, IApplicationAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs index d260ac9..707aeb6 100644 --- a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// Indicates that a page or component can be reused /// - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class CacheAttribute : System.Attribute, IEndpointAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs index 2d7d46f..245d8ae 100644 --- a/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ConditionAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// Activation of options (e.g. WebEx.WebApp.Setting.SystemInformation for displaying system information). /// - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] public class ConditionAttribute : Attribute, IEndpointAttribute where T : class, ICondition { /// diff --git a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs index 4def795..d8554f2 100644 --- a/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ContextPathAttribute.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to define the context path for an application or endpoint. + /// [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] public class ContextPathAttribute : Attribute, IApplicationAttribute, IEndpointAttribute { diff --git a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs index 22af871..40b5754 100644 --- a/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DataPathAttribute.cs @@ -1,5 +1,11 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to specify the data path for an application. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class DataPathAttribute : System.Attribute, IApplicationAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs index 5e67fe4..d7fa97f 100644 --- a/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DefaultAttribute.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// Indicates that a status page ist default /// - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class DefaultAttribute : Attribute, IStatusPageAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs index fc7d507..1668cc2 100644 --- a/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DependencyAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Marks a plugin as dependent on another plugin. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class DependencyAttribute : System.Attribute, IPluginAttribute + public class DependencyAttribute : Attribute, IPluginAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs index 2d6d49c..b33b649 100644 --- a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs @@ -1,5 +1,12 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to provide a description for a class. + /// Implements , , and . + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class DescriptionAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 6317e82..7f8643f 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -1,6 +1,12 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { - public class IconAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute + /// + /// Attribute to specify an icon for a plugin, application, or status page. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class IconAttribute : Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs index da958a4..e67b7bd 100644 --- a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs @@ -1,11 +1,18 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { - public class NameAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute + /// + /// Attribute to assign a name to a class. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class NameAttribute : Attribute, IPluginAttribute, IApplicationAttribute { /// /// Initializes a new instance of the class. /// /// The name. + public NameAttribute(string name) { diff --git a/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs b/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs index 740f8a8..2a548c8 100644 --- a/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/OrderAttribute.cs @@ -1,8 +1,11 @@ -namespace WebExpress.WebCore.WebAttribute +using System; + +namespace WebExpress.WebCore.WebAttribute { /// /// Attribute used to identify a class as a plugin component. /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class OrderAttribute : System.Attribute, IFragmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs index c7616da..ad4a732 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// Attribute to specify the context in which the settings page is associated. /// - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class SettingContextAttribute : Attribute, IEndpointAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs index 78afe5d..987cd46 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// /// Attribute to specify the section where the settings page is listed. /// - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class SettingSectionAttribute : Attribute, IEndpointAttribute { /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 1efe7cb..65afab9 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -110,7 +110,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) - .Where(x => x.GetInterface(typeof(IFragment<>).Name) != null)) + .Where(x => x.GetInterface(typeof(IFragment<,>).Name) != null)) { var id = fragmentType.FullName?.ToLower(); var scopes = new List(); @@ -482,10 +482,14 @@ public IEnumerable GetFragments(IApplicationContext applicatio /// Converts the fragments to HTML for a given section within the specified render context. /// /// The type of the render context. + /// The type of the visual tree. /// The context in which rendering occurs. + /// The visual tree used for rendering. /// The section where the fragment is embedded. /// An enumeration of HTML nodes representing the rendered fragments. - public IEnumerable Render(TRenderContext renderContext, Type section) where TRenderContext : IRenderContext + public IEnumerable Render(TRenderContext renderContext, TVisualTree visualTree, Type section) + where TRenderContext : IRenderContext + where TVisualTree : IVisualTree { var scopes = renderContext?.PageContext?.Scopes ?? [typeof(IScope)]; @@ -499,7 +503,7 @@ public IEnumerable Render(TRenderContext renderContex .SelectMany(x => x.Value) .OrderBy(x => x.Order); - return items.Select(x => x.Render(renderContext)); + return items.Select(x => x.Render(renderContext, visualTree)); } /// diff --git a/src/WebExpress.WebCore/WebFragment/IFragment.cs b/src/WebExpress.WebCore/WebFragment/IFragment.cs index 760297e..fcff1c7 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragment.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragment.cs @@ -7,20 +7,23 @@ namespace WebExpress.WebCore.WebFragment /// /// Represents a fragment that is a part of a web component. /// - public interface IFragment : IFragment + public interface IFragment : IFragment { } /// /// Represents a fragment that is a part of a web component. /// - public interface IFragment : IComponent, IFragmentBase where T : IRenderContext + public interface IFragment : IComponent, IFragmentBase + where TRenderContext : IRenderContext + where TVisualTree : IVisualTree { /// /// Convert the fragment to HTML. /// /// The context in which the fragment is rendered. + /// The visual tree used for rendering the fragment. /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - IHtmlNode Render(T renderContext); + IHtmlNode Render(TRenderContext renderContext, TVisualTree visualTree); } } diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs index 2986e5a..540524b 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentManager.cs @@ -105,9 +105,13 @@ public IEnumerable GetFragments(IApplicationCont /// Converts the fragments to HTML for a given section within the specified render context. /// /// The type of the render context. + /// The type of the visual tree. /// The context in which rendering occurs. + /// The visual tree used for rendering. /// The section where the fragment is embedded. /// An enumeration of HTML nodes representing the rendered fragments. - IEnumerable Render(TRenderContext renderContext, Type section) where TRenderContext : IRenderContext; + IEnumerable Render(TRenderContext renderContext, TVisualTree visualTree, Type section) + where TRenderContext : IRenderContext + where TVisualTree : IVisualTree; } } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs index 9f31854..7c937fa 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs @@ -24,7 +24,7 @@ public bool AddFragmentItem(IPluginContext pluginContext, IApplicationContext ap { var type = fragmentItem.FragmentClass; - if (type.GetInterface(typeof(IFragment<>).Name) == null) + if (type.GetInterface(typeof(IFragment<,>).Name) == null) { return false; } @@ -125,7 +125,7 @@ public IEnumerable GetFragmentItems(IApplicationContext /// An IEnumerable of fragment items public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type fragmentType) { - if (!typeof(IFragment<>).IsAssignableFrom(fragmentType)) + if (!typeof(IFragment<,>).IsAssignableFrom(fragmentType)) { return []; } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index b0f6100..5e3bea4 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -97,9 +97,14 @@ public TFragment CreateInstance() where TFragment : IFragmentBase /// /// Processes the fragments for a given section within the specified render context. /// + /// The type of the render context. + /// The type of the visual tree. /// The context in which rendering occurs. + /// The visual tree to be rendered. /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - public IHtmlNode Render(T renderContext) where T : IRenderContext + public IHtmlNode Render(TRenderContext renderContext, TVisualTree visualTree) + where TRenderContext : IRenderContext + where TVisualTree : IVisualTree { var instance = CreateInstance(); @@ -108,16 +113,19 @@ public IHtmlNode Render(T renderContext) where T : IRenderContext if (!_delegateCache.TryGetValue(FragmentClass, out var del)) { // create and compile the expression - var renderContextType = FragmentClass.GetInterface(typeof(IFragment<>).Name).GetGenericArguments()[0]; + var renderContextType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[0]; + var visualTreeType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[1]; var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); - var renderMethod = FragmentClass.GetMethod("Render", [renderContextType]); + var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var renderMethod = FragmentClass.GetMethod("Render", [renderContextType, visualTreeType]); var callProzessMethod = Expression.Call ( Expression.Constant(instance), renderMethod, - renderContextParam + renderContextParam, + visualTreeParam ); - var lambda = Expression.Lambda(callProzessMethod, renderContextParam) + var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) .Compile(); _delegateCache[FragmentClass] = lambda; @@ -125,7 +133,7 @@ public IHtmlNode Render(T renderContext) where T : IRenderContext } // execute the cached delegate - var html = del.DynamicInvoke(renderContext) as IHtmlNode; + var html = del.DynamicInvoke(renderContext, visualTree) as IHtmlNode; return html; } @@ -138,7 +146,7 @@ public IHtmlNode Render(T renderContext) where T : IRenderContext /// /// The context in which checking occurs. /// True if the fragment is active, false otherwise. - private bool CheckControl(T renderContext) where T : IRenderContext + private bool CheckControl(TRenderContext renderContext) where TRenderContext : IRenderContext { return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 1a009f8..d77e339 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -12,6 +12,7 @@ using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSettingPage.Model; using WebExpress.WebCore.WebUri; @@ -288,6 +289,11 @@ private void Register(IPluginContext pluginContext, IEnumerable x == typeof(IScope)).Any()) + { + scopes.Add(settingPageType); + } + // assign the fragment to existing applications foreach (var applicationContext in applicationContexts) { From 326155353dbfe65909ea2e00d2acc15dc5d4ab48 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 5 Jan 2025 22:50:25 +0100 Subject: [PATCH 124/162] general improvements and bug fixes --- .../Internationalization/de | 6 ++++- .../Internationalization/en | 6 ++++- .../WebApplication/ApplicationManager.cs | 1 + .../WebComponent/ComponentHub.cs | 25 ++++++++----------- .../WebFragment/FragmentManager.cs | 8 ++++-- .../WebPlugin/PluginManager.cs | 12 ++++----- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index cc827e6..0f05a0e 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -39,6 +39,7 @@ componentmanager.wrongtype=Der Typ '{0}' implementiert die Schnittstelle '{1}' n componentmanager.duplicate=Die Komponente '{0}' wurde bereits registriert. componentmanager.remove=Die Komponente '{0}' wurde entfernt. componentmanager.component=Komponenten: +componentmanager.name=Komponente: '{0}' logmanager.initialization=Der Logmanager wurde initialisiert. logmanager.titel=Logmanager @@ -93,6 +94,9 @@ resourcemanager.sitemap=Inhalt der Sitemap für die Anwendung '{0}': resourcemanager.wrongtype=Der Type '{0}' implementiert die Schnittstelle '{1}' nicht. resourcemanager.resource=Ressource: '{0}' für die Anwednung '{1}' +assetmanager.initialization=Der Assetmanager wurde initialisiert. +assetmanager.addresource=Das Asset '{0}' wurde in der Anwendung '{1}' registiert. + pagemanager.initialization=Der Pagemanager wurde initialisiert. pagemanager.addresource=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. pagemanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. @@ -145,7 +149,7 @@ fragmentmanager.wrongtype=Der Typ '{0}' implementiert die Schnittstelle '{1}' ni fragmentmanager.moduleless=Das Fragment '{0}' aus dem Plugin '{1}' besitzt keine Angaben zum Modul. fragmentmanager.addfragment.duplicate=Das Fragment '{0}' aus dem Plugin '{1}' ist bereits hinzugefügt worden. fragmentmanager.titel=Fragmente: -fragmentmanager.fragment={0} +fragmentmanager.fragment=Fragment: '{0}' identitymanager.initialization=Der Identitymanager wurde initialisiert. identitymanager.registerpermission=Die Berechtigung '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index b4cbeac..de3f033 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -39,6 +39,7 @@ componentmanager.wrongtype=The type '{0}' does not implement the interface '{1}' componentmanager.duplicate=The component '{0}' has already been registered. componentmanager.remove=The component '{0}' has been removed. componentmanager.component=Components: +componentmanager.name=Component: '{0}' logmanager.initialization:The log manager has been initialized. logmanager.titel:Log manager @@ -93,6 +94,9 @@ resourcemanager.sitemap=Sitemap content resource '{0}' application: resourcemanager.wrongtype=The type '{0}' does not implement the interface '{1}'. resourcemanager.resource=Resource: '{0}' for application '{1}' +assetmanager.initialization=The asset manager has been initialized. +assetmanager.addresource=The asset '{0}' has been registered in the application '{1}'. + pagemanager.initialization=The page manager has been initialized. pagemanager.addresource=The page '{0}' has been registered in the application '{1}'. pagemanager.addresource.duplicate=The page '{0}' of application '{1}' has already been added. @@ -145,7 +149,7 @@ fragmentmanager.wrongtype=The type '{0}' does not implement the interface '{1}'. fragmentmanager.moduleless=The fragment '{0}' from the plugin '{1}' has no information about the module. fragmentmanager.addfragment.duplicate=The fragment '{0}' from the plugin '{1}' has already been added. fragmentmanager.titel=Fragments: -fragmentmanager.fragment={0} +fragmentmanager.fragment=Fragment: '{0}' identitymanager.initialization=The identity manager has been initialized. identitymanager.registerpermission=The permission '{0}' has been assigned to the application '{1}' and registered in the identity manager. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 0d2f711..7035d00 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -417,6 +417,7 @@ private void Log() { list.Add ( + string.Empty.PadRight(2) + I18N.Translate("webexpress.webcore:applicationmanager.application", applicationContext.ApplicationId) ); } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 97e4760..2099db9 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -50,6 +50,7 @@ public class ComponentHub : IComponentHub private readonly JobManager _jobManager; private readonly TaskManager _taskManager; private readonly IdentityManager _identityManager; + private int _lastCounter = 0; /// /// An event that fires when an component is added. @@ -341,7 +342,7 @@ internal void Register(IPluginContext pluginContext) if (!componentItems.Where(x => x.ComponentId.Equals(id, StringComparison.OrdinalIgnoreCase)).Any()) { - componentItems = componentItems.Concat([ new ComponentItem() + _dictionary[pluginContext] = componentItems.Concat([ new ComponentItem() { ComponentClass = type, ComponentId = id, @@ -364,18 +365,8 @@ internal void Register(IPluginContext pluginContext) ); } } - } - /// - /// Discovers and registers the components from the specified plugins. - /// - /// A list with plugin contexts that contain the components. - public void Register(IEnumerable pluginContexts) - { - foreach (var pluinContext in pluginContexts) - { - Register(pluinContext); - } + Log(); } /// @@ -504,22 +495,28 @@ private void OnRemoveComponent(IComponentManager component) /// private void Log() { + if (_lastCounter == Managers.Count()) + { + return; + } + using var frame = new LogFrameSimple(_httpServerContext.Log); var output = new List { _internationalizationManager.Translate("webexpress.webcore:componentmanager.component") }; - foreach (var pluginContext in PluginManager.Plugins) + foreach (var manager in Managers) { output.Add ( string.Empty.PadRight(2) + - _internationalizationManager.Translate("webexpress.webcore:pluginmanager.plugin", pluginContext.PluginId) + _internationalizationManager.Translate("webexpress.webcore:componentmanager.name", manager.GetType()?.Name.ToLower()) ); } _httpServerContext.Log.Info(string.Join(Environment.NewLine, output)); + _lastCounter = Managers.Count(); } /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 65afab9..4745a5f 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -521,9 +521,13 @@ private void Log() { I18N.Translate("webexpress.webcore:fragmentmanager.titel") }; - foreach (var fragment in Fragments) + foreach (var fragment in Fragments.Distinct()) { - list.Add(I18N.Translate("webexpress.webcore:fragmentmanager.fragment", fragment.FragmentId.ToString())); + list.Add + ( + string.Empty.PadRight(2) + + I18N.Translate("webexpress.webcore:fragmentmanager.fragment", fragment.FragmentId.ToString()) + ); } _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 686ff8f..71588f1 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -186,7 +186,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex _dictionary.Add(id, new PluginItem() { PluginLoadContext = loadContext, - PluginClass = typeof(IPlugin), + PluginClass = assembly.ExportedTypes.FirstOrDefault() ?? typeof(IPlugin), PluginContext = pluginContext, Plugin = null, Dependencies = null, @@ -608,7 +608,7 @@ internal void ShutDown(IEnumerable contexts) /// /// Raises the AddPlugin event. /// - /// The plugin context. + /// The plugin context. private void OnAddPlugin(IPluginContext pluginContext) { AddPlugin?.Invoke(this, pluginContext); @@ -617,7 +617,7 @@ private void OnAddPlugin(IPluginContext pluginContext) /// /// Raises the RemovePlugin event. /// - /// The plugin context. + /// The plugin context. private void OnRemovePlugin(IPluginContext pluginContext) { RemovePlugin?.Invoke(this, pluginContext); @@ -644,7 +644,7 @@ private void Log() x => x.Value.PluginClass.Assembly .GetCustomAttribute() != null ) - .Select(x => I18N.Translate + .Select(x => string.Empty.PadRight(2) + I18N.Translate ( "webexpress.webcore:pluginmanager.pluginmanager.system", x.Key @@ -657,7 +657,7 @@ private void Log() x => x.Value.PluginClass.Assembly .GetCustomAttribute() == null ) - .Select(x => I18N.Translate + .Select(x => string.Empty.PadRight(2) + I18N.Translate ( "webexpress.webcore:pluginmanager.pluginmanager.custom", x.Key @@ -665,7 +665,7 @@ private void Log() ); list.AddRange(_unfulfilledDependencies - .Select(x => I18N.Translate + .Select(x => string.Empty.PadRight(2) + I18N.Translate ( "webexpress.webcore:pluginmanager.pluginmanager.unfulfilleddependencies", x.Key From 173d8b54f930d1c2e2b529a60fcd23f3764e4a81 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 7 Jan 2025 11:29:20 +0100 Subject: [PATCH 125/162] general improvements and bug fixes --- .../Manager/UnitTestFragmentManager.cs | 23 +++++--- .../TestConditionAlwaysFalse.cs | 21 +++++++ src/WebExpress.WebCore.Test/TestFragmentD.cs | 56 +++++++++++++++++++ src/WebExpress.WebCore.Test/TestScopeD.cs | 11 ++++ .../WebFragment/FragmentContext.cs | 2 +- .../WebFragment/IFragmentContext.cs | 2 +- .../WebFragment/Model/FragmentItem.cs | 2 +- 7 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestConditionAlwaysFalse.cs create mode 100644 src/WebExpress.WebCore.Test/TestFragmentD.cs create mode 100644 src/WebExpress.WebCore.Test/TestScopeD.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index f7e5294..8698331 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -22,7 +22,7 @@ public void Register() var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(12, componentHub.FragmentManager.Fragments.Count()); + Assert.Equal(15, componentHub.FragmentManager.Fragments.Count()); } /// @@ -113,11 +113,12 @@ public void GetFragments(Type applicationType, Type scopeType, int count) /// Test the process function of the fragment. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA))] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB))] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB))] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(IScope))] - public void Process(Type applicationType, Type sectionType, Type scopeType) + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA), false)] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB), false)] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB), false)] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(IScope), true)] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeD), true)] + public void Process(Type applicationType, Type sectionType, Type scopeType, bool empty) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -129,7 +130,15 @@ public void Process(Type applicationType, Type sectionType, Type scopeType) var html = componentHub.FragmentManager.Render(renderContext, visualTree, sectionType); Assert.NotNull(html); - Assert.NotEmpty(html.ToString()); + + if (!empty) + { + Assert.NotEmpty(html.FirstOrDefault()?.ToString()); + } + else + { + Assert.Null(html.FirstOrDefault()); + } } } } diff --git a/src/WebExpress.WebCore.Test/TestConditionAlwaysFalse.cs b/src/WebExpress.WebCore.Test/TestConditionAlwaysFalse.cs new file mode 100644 index 0000000..c77a1c1 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestConditionAlwaysFalse.cs @@ -0,0 +1,21 @@ +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test condition that never becomes true. + /// + public class TestConditionAlwaysFalse : ICondition + { + /// + /// Check whether the condition is fulfilled. + /// + /// The request. + /// True if the condition is fulfilled, false otherwise. + public bool Fulfillment(Request request) + { + return false; + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestFragmentD.cs b/src/WebExpress.WebCore.Test/TestFragmentD.cs new file mode 100644 index 0000000..dc7f64d --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestFragmentD.cs @@ -0,0 +1,56 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebFragment; +using WebExpress.WebCore.WebHtml; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test fragment that should never be displayed due to the condition. + /// + [Section()] + [Scope] + [Order(0)] + [Condition] + public sealed class TestFragmentD : IFragment + { + /// + /// Initialization of the fragment. Here, for example, managed resources can be loaded. + /// + /// The component hub. + /// The context of the fragment. + public TestFragmentD(IComponentHub componentHub, IFragmentContext fragmentContext) + { + // test the injection + if (componentHub == null) + { + throw new ArgumentNullException(nameof(componentHub), "Parameter cannot be null or empty."); + } + + // test the injection + if (fragmentContext == null) + { + throw new ArgumentNullException(nameof(fragmentContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processes the fragments in the specified render context. + /// + /// The context in which rendering occurs. + /// The visual tree used for rendering the fragment. + /// An HTML node representing the rendered fragments. + public IHtmlNode Render(TestRenderContext renderContext, TestVisualTree visualTree) + { + return new HtmlText("TestFragmentD"); + } + + /// + /// Disposes the resources used by the fragment. + /// + public void Dispose() + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestScopeD.cs b/src/WebExpress.WebCore.Test/TestScopeD.cs new file mode 100644 index 0000000..c060723 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestScopeD.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebScope; + +namespace WebExpress.WebCore.Test +{ + /// + /// Test scope D implementing the IScope interface. + /// + internal class TestScopeD : IScope + { + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs index d69438c..9994ff5 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentContext.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentContext.cs @@ -31,7 +31,7 @@ public class FragmentContext : IFragmentContext /// /// Returns the conditions that must be met for the component to be active. /// - public ICollection Conditions { get; internal set; } = []; + public IEnumerable Conditions { get; internal set; } = []; /// /// Determines whether the component is created once and reused on each execution. diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs b/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs index 53149fd..fcbc476 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentContext.cs @@ -31,7 +31,7 @@ public interface IFragmentContext : IContext /// /// Returns the conditions that must be met for the component to be active. /// - ICollection Conditions { get; } + IEnumerable Conditions { get; } /// /// Returns the section. diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index 5e3bea4..9be26ab 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -148,7 +148,7 @@ public IHtmlNode Render(TRenderContext renderContex /// True if the fragment is active, false otherwise. private bool CheckControl(TRenderContext renderContext) where TRenderContext : IRenderContext { - return FragmentContext.Conditions.Count == 0 || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); + return !FragmentContext.Conditions.Any() || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); } /// From 417d6aeaccb3048141f3bac4eb37b29d45ee527c Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 7 Jan 2025 19:12:19 +0100 Subject: [PATCH 126/162] chore: update dependencies --- .../WebExpress.WebCore.Test.csproj | 6 +- .../FragmentConditionExtentsion.cs | 31 ++ .../WebFragment/Model/FragmentItem.cs | 340 +++++++++--------- 3 files changed, 204 insertions(+), 173 deletions(-) create mode 100644 src/WebExpress.WebCore/WebFragment/FragmentConditionExtentsion.cs diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index a69126f..d7cd038 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -54,9 +54,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/WebExpress.WebCore/WebFragment/FragmentConditionExtentsion.cs b/src/WebExpress.WebCore/WebFragment/FragmentConditionExtentsion.cs new file mode 100644 index 0000000..e8085db --- /dev/null +++ b/src/WebExpress.WebCore/WebFragment/FragmentConditionExtentsion.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.WebFragment +{ + /// + /// Provides extension methods for checking conditions. + /// + public static class FragmentConditionExtentsion + { + /// + /// Checks if all conditions in the collection are fulfilled for the given request. + /// + /// The collection of conditions to check. + /// The request to evaluate the conditions against. + /// True if all conditions are fulfilled; otherwise, false. + public static bool Check(this IEnumerable conditions, Request request) + { + foreach (var condition in conditions) + { + if (!condition.Fulfillment(request)) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs index 9be26ab..4cc5cfb 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentItem.cs @@ -1,170 +1,170 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using WebExpress.WebCore.WebApplication; -using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebCondition; -using WebExpress.WebCore.WebHtml; -using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebPlugin; - -namespace WebExpress.WebCore.WebFragment.Model -{ - /// - /// Fragments are components that can be integrated into pages to dynamically expand functionalities. - /// - internal class FragmentItem : IDisposable - { - private IFragmentBase _instance; - private readonly IComponentHub _componentHub; - private readonly IHttpServerContext _httpServerContext; - private static readonly Dictionary _delegateCache = []; - - /// - /// Returns the context of the associated plugin. - /// - public IPluginContext PluginContext { get; set; } - - /// - /// Returns the application context. - /// - public IApplicationContext ApplicationContext { get; set; } - - /// - /// Returns the fragment context. - /// - public IFragmentContext FragmentContext { get; set; } - - /// - /// The type of fragment. - /// - public Type FragmentClass { get; set; } - - /// - /// Returns the section. - /// - public Type Section { get; set; } - - /// - /// Returns the scope. - /// - public Type Scope { get; set; } - - /// - /// Returns the conditions that must be met for the component to be active. - /// - public ICollection Conditions { get; set; } - - /// - /// The order of the fragment. - /// - public int Order { get; set; } - - /// - /// Determines whether the component is created once and reused on each execution. - /// - public bool Cache { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The component hub. - /// The context of the HTTP server. - public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerContext) - { - _componentHub = componentHub; - _httpServerContext = httpServerContext; - } - - /// - /// Create the instance of the component. - /// - public TFragment CreateInstance() where TFragment : IFragmentBase - { - var instance = _instance; - - instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); - - if (Cache) - { - _instance = instance; - } - - return (TFragment)instance; - } - - /// - /// Processes the fragments for a given section within the specified render context. - /// - /// The type of the render context. - /// The type of the visual tree. - /// The context in which rendering occurs. - /// The visual tree to be rendered. - /// An HTML node representing the rendered fragments. Can be null if no nodes are present. - public IHtmlNode Render(TRenderContext renderContext, TVisualTree visualTree) - where TRenderContext : IRenderContext - where TVisualTree : IVisualTree - { - var instance = CreateInstance(); - - if (CheckControl(renderContext)) - { - if (!_delegateCache.TryGetValue(FragmentClass, out var del)) - { - // create and compile the expression - var renderContextType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[0]; - var visualTreeType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[1]; - var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); - var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); - var renderMethod = FragmentClass.GetMethod("Render", [renderContextType, visualTreeType]); - var callProzessMethod = Expression.Call - ( - Expression.Constant(instance), - renderMethod, - renderContextParam, - visualTreeParam - ); - var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) - .Compile(); - - _delegateCache[FragmentClass] = lambda; - del = lambda; - } - - // execute the cached delegate - var html = del.DynamicInvoke(renderContext, visualTree) as IHtmlNode; - - return html; - } - - return null; - } - - /// - /// Checks the component to see if they are displayed or disabled. - /// - /// The context in which checking occurs. - /// True if the fragment is active, false otherwise. - private bool CheckControl(TRenderContext renderContext) where TRenderContext : IRenderContext - { - return !FragmentContext.Conditions.Any() || FragmentContext.Conditions.All(x => x.Fulfillment(renderContext?.Request)); - } - - /// - /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. - /// - public void Dispose() - { - } - - /// - /// Convert the resource element to a string. - /// - /// The resource element in its string representation. - public override string ToString() - { - return $"Fragment: '{FragmentContext.FragmentId}'"; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebFragment.Model +{ + /// + /// Fragments are components that can be integrated into pages to dynamically expand functionalities. + /// + internal class FragmentItem : IDisposable + { + private IFragmentBase _instance; + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private static readonly Dictionary _delegateCache = []; + + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; set; } + + /// + /// Returns the fragment context. + /// + public IFragmentContext FragmentContext { get; set; } + + /// + /// The type of fragment. + /// + public Type FragmentClass { get; set; } + + /// + /// Returns the section. + /// + public Type Section { get; set; } + + /// + /// Returns the scope. + /// + public Type Scope { get; set; } + + /// + /// Returns the conditions that must be met for the component to be active. + /// + public ICollection Conditions { get; set; } + + /// + /// The order of the fragment. + /// + public int Order { get; set; } + + /// + /// Determines whether the component is created once and reused on each execution. + /// + public bool Cache { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The context of the HTTP server. + public FragmentItem(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + _httpServerContext = httpServerContext; + } + + /// + /// Create the instance of the component. + /// + public TFragment CreateInstance() where TFragment : IFragmentBase + { + var instance = _instance; + + instance ??= ComponentActivator.CreateInstance(FragmentClass, FragmentContext, _httpServerContext, _componentHub, FragmentContext); + + if (Cache) + { + _instance = instance; + } + + return (TFragment)instance; + } + + /// + /// Processes the fragments for a given section within the specified render context. + /// + /// The type of the render context. + /// The type of the visual tree. + /// The context in which rendering occurs. + /// The visual tree to be rendered. + /// An HTML node representing the rendered fragments. Can be null if no nodes are present. + public IHtmlNode Render(TRenderContext renderContext, TVisualTree visualTree) + where TRenderContext : IRenderContext + where TVisualTree : IVisualTree + { + var instance = CreateInstance(); + + if (CheckConditions(renderContext?.Request)) + { + if (!_delegateCache.TryGetValue(FragmentClass, out var del)) + { + // create and compile the expression + var renderContextType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[0]; + var visualTreeType = FragmentClass.GetInterface(typeof(IFragment<,>).Name).GetGenericArguments()[1]; + var renderContextParam = Expression.Parameter(renderContextType, "renderContext"); + var visualTreeParam = Expression.Parameter(visualTreeType, "visualTree"); + var renderMethod = FragmentClass.GetMethod("Render", [renderContextType, visualTreeType]); + var callProzessMethod = Expression.Call + ( + Expression.Constant(instance), + renderMethod, + renderContextParam, + visualTreeParam + ); + var lambda = Expression.Lambda(callProzessMethod, renderContextParam, visualTreeParam) + .Compile(); + + _delegateCache[FragmentClass] = lambda; + del = lambda; + } + + // execute the cached delegate + var html = del.DynamicInvoke(renderContext, visualTree) as IHtmlNode; + + return html; + } + + return null; + } + + /// + /// Checks the component to see if they are displayed or disabled. + /// + /// The request. + /// True if the fragment is active, false otherwise. + public bool CheckConditions(Request request) + { + return !FragmentContext.Conditions.Any() || FragmentContext.Conditions.All(x => x.Fulfillment(request)); + } + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Convert the resource element to a string. + /// + /// The resource element in its string representation. + public override string ToString() + { + return $"Fragment: '{FragmentContext.FragmentId}'"; + } + } +} From 5b47841ab342d085ab15762418640890dee5902d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 14 Jan 2025 22:34:11 +0100 Subject: [PATCH 127/162] update documentation --- README.md | 3 +++ docs/index.md | 14 ++++++-------- docs/tutorials.md | 2 ++ docs/user-guide.md | 4 +++- src/WebExpress.WebCore/WebFragment/IFragment.cs | 2 +- src/WebExpress.WebCore/WebPage/IVisualTree.cs | 2 +- src/WebExpress.WebCore/WebPage/VisualTree.cs | 2 +- 7 files changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 031bb7b..5e7e6bf 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,15 @@ If you're looking to get started with `WebExpress`, we would recommend using the - [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) - [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) - [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) +- [WebExpress.WebApp API Documentation](https://reneschwarzer.github.io/WebExpress.WebApp/) +- [WebExpress.WebIndex API Documentation](https://reneschwarzer.github.io/WebExpress.WebIndex/) # Learning The following tutorials illustrate the essential techniques of `WebExpress`. These tutorials are designed to assist you, as a developer, in understanding the various aspects of `WebExpress`. Each tutorial provides a detailed, step-by-step guide that you can work through using an example. If you re interested in beginning the development of `WebExpress` components, we would recommend you to complete some of these tutorials. - [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) - [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) +- [WebIndex](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebIndex#readme) # Tags #WebCore #WebExpress #DotNet #NETCore diff --git a/docs/index.md b/docs/index.md index 3824b18..6a2e7de 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,6 @@ ![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) # WebExpress - WebExpress is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .NET language (e.g. C#). Some advantages of WebExpress are: @@ -11,16 +10,15 @@ language (e.g. C#). Some advantages of WebExpress are: - It is fast and efficient and can help you save time and money. - It is flexible and can be customized to meet your specific requirements. -The WebExpress family includes the following projects: +The `WebExpress` family includes the following projects: -- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for WebExpress applications and the documentation. -- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for WebExpress applications. -- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for WebExpress applications. -- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for WebExpress applications. -- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for WebExpress applications. +- [WebExpress](https://github.com/ReneSchwarzer/WebExpress#readme) - The web server for `WebExpress` applications and the documentation. +- [WebExpress.WebCore](https://github.com/ReneSchwarzer/WebExpress.WebCore#readme) - The core for `WebExpress` applications. +- [WebExpress.WebUI](https://github.com/ReneSchwarzer/WebExpress.WebUI#readme) - Common templates and controls for `WebExpress` applications. +- [WebExpress.WebIndex](https://github.com/ReneSchwarzer/WebExpress.WebIndex#readme) - Reverse index for `WebExpress` applications. +- [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for `WebExpress` applications. # WebExpress.WebCore - WebCore is part of the WebExpress family and includes the basic elements of a WebExpress application. # Download diff --git a/docs/tutorials.md b/docs/tutorials.md index 182d1a8..77c2af0 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -8,6 +8,8 @@ our tutorials offer something for everyone. # Getting Started Begin with our basic tutorial: - [HelloWorld](https://github.com/ReneSchwarzer/WebExpress.Tutorial.HelloWorld#readme) +- [WebApp](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebApp#readme) +- [WebIndex](https://github.com/ReneSchwarzer/WebExpress.Tutorial.WebIndex#readme) This tutorial will guide you through the initial steps of creating and running your first `WebExpress` application. diff --git a/docs/user-guide.md b/docs/user-guide.md index b927a5e..3e68bfb 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -5,11 +5,13 @@ Welcome to the `WebExpress.WebCore` User Guide. This guide will help you get sta features. Follow the links below to begin your journey. # Getting started -To get started with WebExpress.WebCore, use the following guides: +To get started with `WebExpress.WebCore`, use the following guides: - [Installation Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/installation_guide.md) - [Development Guide](https://github.com/ReneSchwarzer/WebExpress/blob/main/doc/development_guide.md) - [WebExpress.WebCore API Documentation](https://reneschwarzer.github.io/WebExpress.WebCore/) - [WebExpress.WebUI API Documentation](https://reneschwarzer.github.io/WebExpress.WebUI/) +- [WebExpress.WebApp API Documentation](https://reneschwarzer.github.io/WebExpress.WebApp/) +- [WebExpress.WebIndex API Documentation](https://reneschwarzer.github.io/WebExpress.WebIndex/) We hope you enjoy using `WebExpress.WebCore` and find it valuable for your projects. Happy coding! diff --git a/src/WebExpress.WebCore/WebFragment/IFragment.cs b/src/WebExpress.WebCore/WebFragment/IFragment.cs index fcff1c7..8bd90a4 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragment.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragment.cs @@ -19,7 +19,7 @@ public interface IFragment : IComponent, IFragmentB where TVisualTree : IVisualTree { /// - /// Convert the fragment to HTML. + /// Converts the fragment to an HTML representation. /// /// The context in which the fragment is rendered. /// The visual tree used for rendering the fragment. diff --git a/src/WebExpress.WebCore/WebPage/IVisualTree.cs b/src/WebExpress.WebCore/WebPage/IVisualTree.cs index a869e75..72e5d49 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTree.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebPage public interface IVisualTree { /// - /// Convert to html. + /// Converts to an HTML representation. /// /// The context for rendering the visual tree. /// The page as html. diff --git a/src/WebExpress.WebCore/WebPage/VisualTree.cs b/src/WebExpress.WebCore/WebPage/VisualTree.cs index c69c049..8daed66 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTree.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTree.cs @@ -108,7 +108,7 @@ public virtual void AddHeaderScriptLinks(string url) } /// - /// Convert to html. + /// Converts to an HTML representation. /// /// The context for rendering the visual tree. /// The page as an html tree. From f1397f8a7daa682076373a4dd05996443de0178d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 1 Feb 2025 15:34:02 +0100 Subject: [PATCH 128/162] refactoring --- .../Manager/UnitTestFragmentManager.cs | 4 +- .../WebApplication/ApplicationManager.cs | 144 ++++------ .../WebApplication/IApplicationManager.cs | 10 +- .../Model/ApplicationDictionary.cs | 125 ++++++++- .../WebAsset/AssetManager.cs | 42 +-- .../WebFragment/FragmentManager.cs | 76 +----- .../WebFragment/Model/FragmentDictionary.cs | 170 +++++++++--- .../WebPage/Model/PageDictionary.cs | 254 ++++++++++++++---- src/WebExpress.WebCore/WebPage/PageManager.cs | 85 ++---- 9 files changed, 555 insertions(+), 355 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 8698331..82b411b 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -110,7 +110,7 @@ public void GetFragments(Type applicationType, Type scopeType, int count) } /// - /// Test the process function of the fragment. + /// Test the Render function of the fragment. /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA), false)] @@ -118,7 +118,7 @@ public void GetFragments(Type applicationType, Type scopeType, int count) [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB), false)] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(IScope), true)] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeD), true)] - public void Process(Type applicationType, Type sectionType, Type scopeType, bool empty) + public void Render(Type applicationType, Type sectionType, Type scopeType, bool empty) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index 7035d00..c53905c 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -21,7 +22,7 @@ public sealed class ApplicationManager : IApplicationManager, IExecutableElement { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly ApplicationDictionary _dictionary = []; + private readonly ApplicationDictionary _dictionary = new(); /// /// An event that fires when an application is added. @@ -36,13 +37,14 @@ public sealed class ApplicationManager : IApplicationManager, IExecutableElement /// /// Returns the stored applications. /// - public IEnumerable Applications => _dictionary.Values.SelectMany(x => x.Values).Select(x => x.ApplicationContext); + public IEnumerable Applications => _dictionary.All; /// /// Initializes a new instance of the class. /// /// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private ApplicationManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -65,15 +67,12 @@ private ApplicationManager(IComponentHub componentHub, IHttpServerContext httpSe private void Register(IPluginContext pluginContext) { // the plugin has already been registered - if (_dictionary.ContainsKey(pluginContext)) + if (_dictionary.Contains(pluginContext)) { return; } - _dictionary.Add(pluginContext, []); - var assembly = pluginContext.Assembly; - var pluginDict = _dictionary[pluginContext]; foreach (var type in assembly.GetExportedTypes().Where ( @@ -143,15 +142,13 @@ private void Register(IPluginContext pluginContext) _componentHub ); - if (!pluginDict.ContainsKey(id)) + if (_dictionary.AddApplication(pluginContext, new ApplicationItem() + { + ApplicationClass = type, + ApplicationContext = applicationContext, + Application = applicationInstance + })) { - pluginDict.Add(id, new ApplicationItem() - { - ApplicationClass = type, - ApplicationContext = applicationContext, - Application = applicationInstance - }); - _httpServerContext.Log.Debug ( I18N.Translate("webexpress.webcore:applicationmanager.register", id) @@ -183,43 +180,26 @@ internal void Remove(IPluginContext pluginContext) return; } - if (_dictionary.TryGetValue(pluginContext, out var value)) + foreach (var applicationContext in _dictionary.RemoveApplications(pluginContext)) { - foreach (var applicationContext in value) - { - OnRemoveApplication(applicationContext.Value.ApplicationContext); - } - - _dictionary.Remove(pluginContext); + OnRemoveApplication(applicationContext); } Log(); } /// - /// Determines the application contexts for a given application id. + /// Returns the application context for a given application id. /// /// The application id. - /// The context of the application or null. + /// The context of the application or null if the application id is null, empty, or not found. public IApplicationContext GetApplication(string applicationId) { - if (string.IsNullOrWhiteSpace(applicationId)) return null; - - var items = _dictionary.Values - .Where(x => x.ContainsKey(applicationId.ToLower())) - .Select(x => x[applicationId.ToLower()]) - .FirstOrDefault(); - - if (items != null) - { - return items.ApplicationContext; - } - - return null; + return _dictionary.GetApplication(applicationId); } /// - /// Determines the application contexts for a given application id. + /// Returns the application contexts for a given application id. /// /// The application type. /// The context of the application or null. @@ -229,7 +209,7 @@ public IApplicationContext GetApplication() } /// - /// Determines the application contexts for the given application ids. + /// Returns the application contexts for the given application ids. /// /// The applications ids. Can contain regular expressions or * for all. /// The contexts of the applications as an enumeration. @@ -261,34 +241,23 @@ public IEnumerable GetApplications(IEnumerable appl } /// - /// Determines the application contexts for the given plugin. + /// Returns the application contexts for the given plugin. /// /// The context of the plugin. /// The contexts of the applications as an enumeration. public IEnumerable GetApplications(IPluginContext pluginContext) { - if (_dictionary.TryGetValue(pluginContext, out var value)) - { - return value.Values.Select(x => x.ApplicationContext); - } - - return []; + return _dictionary.GetApplications(pluginContext); } /// - /// Determines the application contexts for a given application type. + /// Returns the application contexts for a given application type. /// /// The application type. /// The contexts of the applications as an enumeration. public IEnumerable GetApplications(Type application) { - if (application == null) return null; - - var items = _dictionary.Values.SelectMany(x => x.Values) - .Where(x => x.ApplicationClass.Equals(application) || application.IsAssignableFrom(x.ApplicationClass)) - .Select(x => x.ApplicationContext); - - return items; + return _dictionary.GetApplications(application); } /// @@ -301,40 +270,7 @@ public void Boot(IPluginContext pluginContext) { return; } - - if (_dictionary.TryGetValue(pluginContext, out var value)) - { - foreach (var applicationItem in value?.Values ?? Enumerable.Empty()) - { - var token = applicationItem.CancellationTokenSource.Token; - - // Run the application concurrently - Task.Run(() => - { - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress.webcore:applicationmanager.application.processing.start", - applicationItem.ApplicationContext.ApplicationId) - ); - - applicationItem.Application.Run(); - - _httpServerContext.Log.Debug - ( - I18N.Translate - ( - "webexpress.webcore:applicationmanager.application.processing.end", - applicationItem.ApplicationContext.ApplicationId - ) - ); - - token.ThrowIfCancellationRequested(); - }, token); - } - } - else + else if (!_dictionary.Contains(pluginContext)) { _httpServerContext.Log.Warning ( @@ -344,6 +280,38 @@ public void Boot(IPluginContext pluginContext) pluginContext.PluginId ) ); + + return; + } + + foreach (var applicationItem in _dictionary.GetApplicationItems(pluginContext)) + { + var token = applicationItem.CancellationTokenSource.Token; + + // Run the application concurrently + Task.Run(() => + { + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress.webcore:applicationmanager.application.processing.start", + applicationItem.ApplicationContext.ApplicationId) + ); + + applicationItem.Application.Run(); + + _httpServerContext.Log.Debug + ( + I18N.Translate + ( + "webexpress.webcore:applicationmanager.application.processing.end", + applicationItem.ApplicationContext.ApplicationId + ) + ); + + token.ThrowIfCancellationRequested(); + }, token); } } @@ -353,7 +321,7 @@ public void Boot(IPluginContext pluginContext) /// The context of the plugin that contains the applications. public void ShutDown(IPluginContext pluginContext) { - foreach (var applicationItem in _dictionary[pluginContext]?.Values ?? Enumerable.Empty()) + foreach (var applicationItem in _dictionary.GetApplicationItems(pluginContext)) { applicationItem.CancellationTokenSource.Cancel(); } diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs index 99fe6e2..ed01437 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationManager.cs @@ -26,35 +26,35 @@ public interface IApplicationManager : IComponentManager IEnumerable Applications { get; } /// - /// Determines the application contexts for a given application id. + /// Returns the application contexts for a given application id. /// /// The application id. /// The context of the application or null. IApplicationContext GetApplication(string applicationId); /// - /// Determines the application contexts for a given application id. + /// Returns the application contexts for a given application id. /// /// The application type. /// The context of the application or null. IApplicationContext GetApplication(); /// - /// Determines the application contexts for the given application ids. + /// Returns the application contexts for the given application ids. /// /// The applications ids. Can contain regular expressions or * for all. /// The contexts of the applications as an enumeration. IEnumerable GetApplications(IEnumerable applicationIds); /// - /// Determines the application contexts for the given plugin. + /// Returns the application contexts for the given plugin. /// /// The context of the plugin. /// The contexts of the applications as an enumeration. IEnumerable GetApplications(IPluginContext pluginContext); /// - /// Determines the application contexts for a given application type. + /// Returns the application contexts for a given application type. /// /// The application type. /// The contexts of the applications as an enumeration. diff --git a/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs b/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs index 93e6801..d1cff54 100644 --- a/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs +++ b/src/WebExpress.WebCore/WebApplication/Model/ApplicationDictionary.cs @@ -1,13 +1,130 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebApplication.Model { /// - /// Key = Plugin context - /// Value = { Key = application id, Value = application item } + /// Represents a dictionary that maps plugin contexts to application items. /// - internal class ApplicationDictionary : Dictionary> + internal class ApplicationDictionary { + private readonly Dictionary> _dict = []; + + /// + /// Returns all application contexts from the dictionary. + /// + public IEnumerable All => _dict + .Values.SelectMany(x => x.Values) + .Select(x => x.ApplicationContext); + + /// + /// Adds a application item to the dictionary. + /// + /// The plugin context. + /// The application item. + /// True if the application item was added successfully, false if an element with the same status code already exists. + public bool AddApplication(IPluginContext pluginContext, ApplicationItem applicationItem) + { + if (!_dict.TryGetValue(pluginContext, out var applicationDict)) + { + applicationDict = []; + _dict[pluginContext] = applicationDict; + } + + if (applicationDict.TryAdd(applicationItem.ApplicationContext.ApplicationId, applicationItem)) + { + return true; + } + + return false; + } + + /// + /// Removes applications from the dictionary. + /// + /// The plugin context. + /// An IEnumerable of application contexts that were removed. + public IEnumerable RemoveApplications(IPluginContext pluginContext) + { + var applicationContexts = GetApplications(pluginContext); + + _dict.Remove(pluginContext); + + return applicationContexts; + } + + /// + /// Returns the application contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of application contexts associated with the given plugin context. + public IEnumerable GetApplicationItems(IPluginContext pluginContext) + { + var applicationItems = _dict + .Where(x => x.Key == pluginContext) + .Select(x => x.Value) + .Select(x => x.Values) + .SelectMany(x => x); + + return applicationItems; + } + + /// + /// Returns the application contexts for a given plugin context. + /// + /// The plugin context. + /// An IEnumerable of application contexts associated with the given plugin context. + public IEnumerable GetApplications(IPluginContext pluginContext) + { + var applicationContexts = GetApplicationItems(pluginContext) + .Select(x => x.ApplicationContext); + + return applicationContexts; + } + + /// + /// Returns the application context for a given application id. + /// + /// The application id. + /// The context of the application or null if the application id is null, empty, or not found. + public IApplicationContext GetApplication(string applicationId) + { + if (string.IsNullOrWhiteSpace(applicationId)) return null; + + var items = _dict.Values + .Where(x => x.ContainsKey(applicationId.ToLower())) + .Select(x => x[applicationId.ToLower()]) + .FirstOrDefault(); + + return items?.ApplicationContext; + } + + /// + /// Returns the application contexts for a given application type. + /// + /// The application type. + /// The contexts of the applications as an enumeration. + public IEnumerable GetApplications(Type application) + { + if (application == null) return []; + + var items = _dict.Values.SelectMany(x => x.Values) + .Where(x => x.ApplicationClass.Equals(application) || application.IsAssignableFrom(x.ApplicationClass)) + .Select(x => x.ApplicationContext); + + return items; + } + + /// + /// Checks if the dictionary contains the specified plugin context. + /// + /// The plugin context to check for. + /// True if the plugin context exists in the dictionary, otherwise false. + public bool Contains(IPluginContext pluginContext) + { + return _dict.ContainsKey(pluginContext); + } } } diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 4bb1a09..8c1c5c7 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; @@ -46,6 +47,7 @@ public sealed class AssetManager : IAssetManager, ISystemComponent /// /// The component hub. /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { _componentHub = componentHub; @@ -228,23 +230,6 @@ internal void Remove(IApplicationContext applicationContext) } } - /// - /// Returns an enumeration of all containing asset items of a plugin. - /// - /// A context of a plugin whose resources are to be registered. - /// An enumeration of resource items. - private IEnumerable GetAssetItems(IPluginContext pluginContext) - { - if (_itemDictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x); - } - - return []; - } - /// /// Returns an enumeration of all containing asset contexts of a plugin. /// @@ -276,29 +261,6 @@ public IEnumerable GetAssets(IApplicationContext applicationConte .Select(x => x.AssetContext); } - /// - /// Creates a new resource and returns it. If a resource already exists (through caching), the existing instance is returned. - /// - /// The context used for asset creation. - /// The created or cached resource. - private IAsset CreateAssetInstance(IAssetContext assetContext) - { - var resourceItem = _itemDictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x) - .FirstOrDefault(x => x.AssetContext.Equals(assetContext)); - - if (resourceItem != null && resourceItem.Instance == null) - { - var instance = ComponentActivator.CreateInstance(resourceItem.AssetClass, assetContext, _httpServerContext, _componentHub); - resourceItem.Instance = instance; - - return instance; - } - - return resourceItem?.Instance as IAsset; - } - /// /// Raises the AddResource event. /// diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index 4745a5f..ff75707 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -24,7 +24,7 @@ public sealed class FragmentManager : IFragmentManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly FragmentDictionary _dictionary = []; + private readonly FragmentDictionary _dictionary = new(); /// /// An event that fires when an fragment is added. @@ -39,12 +39,7 @@ public sealed class FragmentManager : IFragmentManager /// /// Returns the collection of fragment contexts. /// - public IEnumerable Fragments => _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x) - .Select(x => x.FragmentContext); + public IEnumerable Fragments => _dictionary.All; /// /// Initializes a new instance of the class. @@ -74,7 +69,7 @@ private FragmentManager(IComponentHub componentHub, IHttpServerContext httpServe /// The context of the plugin whose fragments are to be associated. private void Register(IPluginContext pluginContext) { - if (_dictionary.ContainsKey(pluginContext)) + if (_dictionary.Contains(pluginContext)) { return; } @@ -90,7 +85,7 @@ private void Register(IApplicationContext applicationContext) { foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) { - if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + if (_dictionary.Contains(pluginContext, applicationContext)) { continue; } @@ -348,14 +343,7 @@ public IEnumerable GetFragments() where T : IFragmentBase /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(Type fragmentType) { - return _dictionary.Values - .SelectMany(x => x) - .SelectMany(x => x.Value) - .SelectMany(x => x.Value) - .SelectMany(x => x.Value) - .Where(x => x.FragmentClass == fragmentType) - .OrderBy(x => x.Order) - .Select(x => x.FragmentContext); + return _dictionary.GetFragments(fragmentType); } /// @@ -377,15 +365,7 @@ public IEnumerable GetFragments(IApplicationContext /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(IApplicationContext applicationContext, Type fragmentType) { - return _dictionary.Values - .SelectMany(x => x) - .Where(x => x.Key == applicationContext) - .SelectMany(x => x.Value) - .SelectMany(x => x.Value) - .SelectMany(x => x.Value) - .Where(x => x.FragmentClass == fragmentType) - .OrderBy(x => x.Order) - .Select(x => x.FragmentContext); + return _dictionary.GetFragments(applicationContext, fragmentType); } /// @@ -411,18 +391,7 @@ public IEnumerable GetFragments(IApplication /// An enumeration of the filtered fragment contexts. public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope) { - scope ??= typeof(IScope); - - return _dictionary.Values - .SelectMany(x => x) - .Where(x => x.Key == applicationContext) - .SelectMany(x => x.Value) - .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) - .SelectMany(x => x.Value) - .Where(x => x.Key == scope) - .SelectMany(x => x.Value) - .OrderBy(x => x.Order) - .Select(x => x.FragmentContext); + return _dictionary.GetFragments(applicationContext, section, scope); } /// @@ -439,22 +408,9 @@ public IEnumerable GetFragments(IApplicationCont { var effectiveScopes = (scopes?.Any() == true) ? scopes : [typeof(IScope)]; - foreach (var scope in effectiveScopes) + foreach (var item in _dictionary.GetFragmentItems(applicationContext, typeof(TFragment), typeof(TSection), effectiveScopes)) { - foreach (var item in _dictionary.Values - .SelectMany(x => x) - .Where(x => x.Key == applicationContext) - .SelectMany(x => x.Value) - .Where(x => x.Key == typeof(TSection) || typeof(TSection).IsAssignableFrom(x.Key)) - .SelectMany(x => x.Value) - .Where(x => x.Key == scope) - .SelectMany(x => x.Value) - .Where(x => x.FragmentClass == typeof(TFragment) || typeof(TFragment).IsAssignableFrom(x.FragmentClass)) - .OrderBy(x => x.Order) - ) - { - yield return item.CreateInstance(); - } + yield return item.CreateInstance(); } } @@ -471,7 +427,7 @@ public IEnumerable GetFragments(IApplicationContext applicatio foreach (var scope in effectiveScopes) { - foreach (var item in GetFragments(applicationContext, section, scope)) + foreach (var item in GetFragments(applicationContext, section, effectiveScopes)) { yield return item; } @@ -491,17 +447,9 @@ public IEnumerable Render(TRenderContext where TRenderContext : IRenderContext where TVisualTree : IVisualTree { + var applicationContext = renderContext?.PageContext?.ApplicationContext; var scopes = renderContext?.PageContext?.Scopes ?? [typeof(IScope)]; - - var items = _dictionary.Values - .SelectMany(x => x) - .Where(x => x.Key == renderContext?.PageContext?.ApplicationContext) - .SelectMany(x => x.Value) - .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) - .SelectMany(x => x.Value) - .Where(x => scopes.Any(y => x.Key == y)) - .SelectMany(x => x.Value) - .OrderBy(x => x.Order); + var items = _dictionary.GetFragmentItems(applicationContext, section, scopes); return items.Select(x => x.Render(renderContext, visualTree)); } diff --git a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs index 7c937fa..1213440 100644 --- a/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs +++ b/src/WebExpress.WebCore/WebFragment/Model/FragmentDictionary.cs @@ -3,6 +3,7 @@ using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebScope; namespace WebExpress.WebCore.WebFragment.Model { @@ -11,8 +12,20 @@ namespace WebExpress.WebCore.WebFragment.Model /// which in turn maps to a dictionary of section types and inner maps of scope types and lists of FragmentItem objects. /// Plugin -> Application -> Section -> Scope -> FragmentItem /// - internal class FragmentDictionary : Dictionary>>>> + internal class FragmentDictionary { + private readonly Dictionary>>>> _dict = []; + + /// + /// Returns all fragment contexts from the dictionary. + /// + public IEnumerable All => _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.FragmentContext); + /// /// Adds a fragment item to the dictionary. /// @@ -29,10 +42,10 @@ public bool AddFragmentItem(IPluginContext pluginContext, IApplicationContext ap return false; } - if (!TryGetValue(pluginContext, out var applicationDict)) + if (!_dict.TryGetValue(pluginContext, out var applicationDict)) { applicationDict = []; - this[pluginContext] = applicationDict; + _dict[pluginContext] = applicationDict; } if (!applicationDict.TryGetValue(applicationContext, out var sectionDict)) @@ -60,7 +73,7 @@ public bool AddFragmentItem(IPluginContext pluginContext, IApplicationContext ap return true; } - return false; // item with the same fragment class already exists + return false; } /// @@ -72,7 +85,7 @@ public IEnumerable RemoveFragments(IPluginContext pluginContex { var fragments = GetFragments(pluginContext); - Remove(pluginContext); + _dict.Remove(pluginContext); return fragments; } @@ -84,7 +97,7 @@ public IEnumerable RemoveFragments(IPluginContext pluginContex /// An IEnumerable of fragment contexts that were removed. public IEnumerable RemoveFragments(IApplicationContext applicationContext) { - foreach (var pluginKeyValue in this) + foreach (var pluginKeyValue in _dict) { if (pluginKeyValue.Value.TryGetValue(applicationContext, out var sectionDict)) { @@ -92,7 +105,7 @@ public IEnumerable RemoveFragments(IApplicationContext applica if (pluginKeyValue.Value.Count == 0) { - Remove(pluginKeyValue.Key); + _dict.Remove(pluginKeyValue.Key); } foreach (var item in sectionDict.Values @@ -107,36 +120,45 @@ public IEnumerable RemoveFragments(IApplicationContext applica } /// - /// Returns the fragment items from the dictionary. + /// Returns all fragment contexts that belong to a given application. /// - /// The type of fragment. /// The application context. - /// An IEnumerable of fragment items - public IEnumerable GetFragmentItems(IApplicationContext applicationContext) where TFragment : IFragmentBase + /// The section where the fragment is embedded. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type section, IEnumerable scopes) { - return GetFragmentItems(applicationContext, typeof(TFragment)); + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) + .SelectMany(x => x.Value) + .Where(x => scopes.Any(y => x.Key == y)) + .SelectMany(x => x.Value) + .OrderBy(x => x.Order); } /// - /// Returns the fragment items from the dictionary. + /// Returns all fragment items that belong to a given application context, fragment type, section, and scopes. /// /// The application context. - /// The type of fragment. - /// An IEnumerable of fragment items - public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type fragmentType) + /// The type of fragment. + /// The section where the fragment is embedded. + /// The scopes where the fragment is embedded. + /// An enumeration of the filtered fragment items. + public IEnumerable GetFragmentItems(IApplicationContext applicationContext, Type fragment, Type section, IEnumerable scopes) { - if (!typeof(IFragment<,>).IsAssignableFrom(fragmentType)) - { - return []; - } - - return Values - .Where(x => x.ContainsKey(applicationContext)) - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x) - .Where(x => x.FragmentClass == fragmentType); + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) + .SelectMany(x => x.Value) + .Where(x => scopes.Any(y => x.Key == y)) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == fragment || fragment.IsAssignableFrom(x.FragmentClass)) + .OrderBy(x => x.Order); } /// @@ -146,12 +168,92 @@ public IEnumerable GetFragmentItems(IApplicationContext applicatio /// An IEnumerable of fragment contexts. public IEnumerable GetFragments(IPluginContext pluginContext) { - return this.Where(x => x.Key == pluginContext) - .SelectMany(x => x.Value.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .SelectMany(x => x) - .Select(x => x.FragmentContext); + return _dict.Where(x => x.Key == pluginContext) + .SelectMany(x => x.Value.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(Type fragmentType) + { + return _dict.Values + .SelectMany(x => x) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == fragmentType) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given fragment type. + /// + /// The application context. + /// The fragment type. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, Type fragmentType) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .SelectMany(x => x.Value) + .Where(x => x.FragmentClass == fragmentType) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Returns all fragment contexts that belong to a given application. + /// + /// The application context. + /// The section where the fragment is embedded. + /// The scope where the fragment is embedded. + /// An enumeration of the filtered fragment contexts. + public IEnumerable GetFragments(IApplicationContext applicationContext, Type section, Type scope) + { + scope ??= typeof(IScope); + + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.Key == section || section.IsAssignableFrom(x.Key)) + .SelectMany(x => x.Value) + .Where(x => x.Key == scope) + .SelectMany(x => x.Value) + .OrderBy(x => x.Order) + .Select(x => x.FragmentContext); + } + + /// + /// Checks if the dictionary contains the specified plugin context. + /// + /// The plugin context to check for. + /// True if the plugin context exists in the dictionary, otherwise false. + public bool Contains(IPluginContext pluginContext) + { + return _dict.ContainsKey(pluginContext); + } + + /// + /// Checks if the dictionary contains the specified plugin context and application context. + /// + /// The plugin context to check for. + /// The application context to check for. + /// True if the plugin context and application context exist in the dictionary, otherwise false. + public bool Contains(IPluginContext pluginContext, IApplicationContext applicationContext) + { + return _dict.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext); } } } diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs index a0f4e07..207b562 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; @@ -10,8 +11,18 @@ namespace WebExpress.WebCore.WebPage.Model /// key = plugin context /// value = application context { key = page type, value = page item } /// - internal class PageDictionary : Dictionary>> + internal class PageDictionary { + private readonly Dictionary>> _dict = []; + + /// + /// Returns all page contexts. + /// + public IEnumerable All => _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Select(x => x.PageContext); + /// /// Adds a page item to the dictionary. /// @@ -28,81 +39,112 @@ public bool AddPageItem(IPluginContext pluginContext, IApplicationContext applic return false; } - if (!ContainsKey(pluginContext)) + if (!_dict.TryGetValue(pluginContext, out Dictionary> appContextDict)) { - this[pluginContext] = []; + appContextDict = ([]); + _dict[pluginContext] = appContextDict; } - var appContextDict = this[pluginContext]; - - if (!appContextDict.ContainsKey(applicationContext)) + if (!appContextDict.TryGetValue(applicationContext, out Dictionary pageDict)) { - appContextDict[applicationContext] = []; + pageDict = ([]); + appContextDict[applicationContext] = pageDict; } - var pageDict = appContextDict[applicationContext]; - if (!pageDict.ContainsKey(type)) { pageDict[type] = pageItem; return true; } - return false; // item with the same page class already exists + return false; } /// - /// Removes a page from the dictionary. + /// Removes all page from the dictionary. /// /// The plugin context. - /// The application context. - public void RemovePage(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IPage + public IEnumerable RemovePage(IPluginContext pluginContext) { - var type = typeof(T); + var removed = GetPageItems(pluginContext); + + _dict.Remove(pluginContext); - if (ContainsKey(pluginContext)) + foreach (var item in removed) { - var appContextDict = this[pluginContext]; + item.Dispose(); + } - if (appContextDict.ContainsKey(applicationContext)) - { - var pageDict = appContextDict[applicationContext]; + return removed.Select(x => x.PageContext); + } - if (pageDict.ContainsKey(type)) - { - pageDict.Remove(type); + /// + /// Removes all page from the dictionary. + /// + /// The application context. + public IEnumerable RemovePage(IApplicationContext applicationContext) + { + var removed = GetPageItems(applicationContext); - if (pageDict.Count == 0) - { - appContextDict.Remove(applicationContext); + foreach (var applicationDict in _dict.Values) + { + applicationDict.Remove(applicationContext); + } - if (appContextDict.Count == 0) - { - Remove(pluginContext); - } - } - } - } + foreach (var item in removed) + { + item.Dispose(); } + + return removed.Select(x => x.PageContext); } /// - /// Returns the page items from the dictionary. + /// Returns the page items associated with the specified plugin context. /// - /// The type of page. - /// The application context. - /// An IEnumerable of page items - public IEnumerable GetPageItems(IApplicationContext applicationContext) where T : IPage + /// The plugin context to retrieve page items for. + /// An IEnumerable of associated with the specified application context. + public IEnumerable GetPageItems(IPluginContext pluginContext) + { + return _dict.Where(x => x.Key.Equals(pluginContext)) + .Select(x => x.Value) + .SelectMany(x => x.Values) + .SelectMany(x => x.Values); + } + + /// + /// Returns the page items associated with the specified application context. + /// + /// The application context to retrieve page items for. + /// An IEnumerable of associated with the specified application context. + public IEnumerable GetPageItems(IApplicationContext applicationContext) { - return GetPageItems(applicationContext, typeof(T)); + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key.Equals(applicationContext)) + .SelectMany(x => x.Value) + .Select(x => x.Value); } /// - /// Returns the page items from the dictionary. + /// Returns the page item associated with the specified page context. + /// + /// The context of the page to retrieve. + /// The associated with the specified page context, or null if no such item exists. + public PageItem GetPageItem(IPageContext pageContext) + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .FirstOrDefault(x => x.PageContext.Equals(pageContext)); + } + + /// + /// Returns the page items from the dictionary for a specific application context and page type. /// /// The application context. - /// The type of page. - /// An IEnumerable of page items + /// The type of the page. + /// An IEnumerable of page items. public IEnumerable GetPageItems(IApplicationContext applicationContext, Type pageType) { if (!typeof(IPage).IsAssignableFrom(pageType)) @@ -110,22 +152,138 @@ public IEnumerable GetPageItems(IApplicationContext applicationContext return []; } - if (ContainsKey(applicationContext?.PluginContext)) + if (_dict.ContainsKey(applicationContext?.PluginContext)) { - var appContextDict = this[applicationContext?.PluginContext]; + var appContextDict = _dict[applicationContext?.PluginContext]; - if (appContextDict.ContainsKey(applicationContext)) + if (appContextDict.TryGetValue(applicationContext, out Dictionary pageDict)) { - var pageDict = appContextDict[applicationContext]; - - if (pageDict.ContainsKey(pageType)) + if (pageDict.TryGetValue(pageType, out PageItem value)) { - return [pageDict[pageType]]; + return [value]; } } } return []; } + + /// + /// Returns an enumeration of all containing page contexts of a plugin. + /// + /// A context of a plugin whose pages are to be registered. + /// An enumeration of page contexts. + public IEnumerable GetPages(IPluginContext pluginContext) + { + if (_dict.TryGetValue(pluginContext, out var pluginResources)) + { + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.Value.PageContext); + } + + return []; + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// An enumeration of page contextes. + public IEnumerable GetPages(Type pageType) + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.PageClass.Equals(pageType)) + .Select(x => x.PageContext); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the application. + /// An enumeration of page contextes. + public IEnumerable GetPages(Type pageType, IApplicationContext applicationContext) + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.PageClass.Equals(pageType)) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.PageContext); + } + + /// + /// Returns an enumeration of page contextes. + /// + /// The page type. + /// The context of the application. + /// An enumeration of page contextes. + public IEnumerable GetPages(IApplicationContext applicationContext) where TPage : IPage + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.PageClass.Equals(typeof(TPage))) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.PageContext); + } + + /// + /// Returns the page context. + /// + /// The context of the application. + /// The page id. + /// An page context or null. + public IPageContext GetPage(IApplicationContext applicationContext, string pageId) + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) + .Where(x => x.PageContext.EndpointId.Equals(pageId)) + .Select(x => x.PageContext) + .FirstOrDefault(); + } + + /// + /// Returns the page context. + /// + /// The application id. + /// The page id. + /// An page context or null. + public IPageContext GetPage(string applicationId, string pageId) + { + return _dict.Values + .SelectMany(x => x.Values) + .SelectMany(x => x.Values) + .Where(x => x.PageContext.ApplicationContext.ApplicationId.Equals(applicationId)) + .Where(x => x.PageContext.EndpointId.Equals(pageId)) + .Select(x => x.PageContext) + .FirstOrDefault(); + } + + /// + /// Checks if the dictionary contains the specified plugin context. + /// + /// The plugin context to check for. + /// True if the plugin context exists in the dictionary, otherwise false. + public bool Contains(IPluginContext pluginContext) + { + return _dict.ContainsKey(pluginContext); + } + + // + /// Checks if the dictionary contains the specified plugin context and application context. + /// + /// The plugin context to check for. + /// The application context to check for. + /// True if the plugin context and application context exist in the dictionary, otherwise false. + public bool Contains(IPluginContext pluginContext, IApplicationContext applicationContext) + { + return _dict.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext); + } } } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index a18ff96..ae92873 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -25,7 +25,7 @@ public class PageManager : IPageManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly PageDictionary _dictionary = []; + private readonly PageDictionary _dictionary = new(); private static readonly Dictionary _delegateCache = []; /// @@ -41,10 +41,7 @@ public class PageManager : IPageManager /// /// Returns all page contexts. /// - public IEnumerable Pages => _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Select(x => x.PageContext); + public IEnumerable Pages => _dictionary.All; /// /// Initializes a new instance of the class. @@ -161,14 +158,7 @@ private PageManager(IComponentHub componentHub, IHttpServerContext httpServerCon /// An enumeration of page contexts. public IEnumerable GetPages(IPluginContext pluginContext) { - if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x.Value.PageContext); - } - - return []; + return _dictionary.GetPages(pluginContext); } /// @@ -188,11 +178,7 @@ public IEnumerable GetPages() where T : IPage /// An enumeration of page contextes. public IEnumerable GetPages(Type pageType) { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Where(x => x.PageClass.Equals(pageType)) - .Select(x => x.PageContext); + return _dictionary.GetPages(pageType); } /// @@ -203,12 +189,7 @@ public IEnumerable GetPages(Type pageType) /// An enumeration of page contextes. public IEnumerable GetPages(Type pageType, IApplicationContext applicationContext) { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Where(x => x.PageClass.Equals(pageType)) - .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) - .Select(x => x.PageContext); + return _dictionary.GetPages(pageType, applicationContext); } /// @@ -219,12 +200,7 @@ public IEnumerable GetPages(Type pageType, IApplicationContext app /// An enumeration of page contextes. public IEnumerable GetPages(IApplicationContext applicationContext) where T : IPage { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Where(x => x.PageClass.Equals(typeof(T))) - .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) - .Select(x => x.PageContext); + return _dictionary.GetPages(applicationContext); } /// @@ -235,13 +211,7 @@ public IEnumerable GetPages(IApplicationContext applicationCont /// An page context or null. public IPageContext GetPage(IApplicationContext applicationContext, string pageId) { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Where(x => x.PageContext.ApplicationContext.Equals(applicationContext)) - .Where(x => x.PageContext.EndpointId.Equals(pageId)) - .Select(x => x.PageContext) - .FirstOrDefault(); + return _dictionary.GetPage(applicationContext, pageId); } /// @@ -252,13 +222,7 @@ public IPageContext GetPage(IApplicationContext applicationContext, string pageI /// An page context or null. public IPageContext GetPage(string applicationId, string pageId) { - return _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .Where(x => x.PageContext.ApplicationContext.ApplicationId.Equals(applicationId)) - .Where(x => x.PageContext.EndpointId.Equals(pageId)) - .Select(x => x.PageContext) - .FirstOrDefault(); + return _dictionary.GetPage(applicationId, pageId); } /// @@ -268,10 +232,7 @@ public IPageContext GetPage(string applicationId, string pageId) /// The created or cached page. private IEndpoint CreatePageInstance(IPageContext pageContext) { - var resourceItem = _dictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x.Values) - .FirstOrDefault(x => x.PageContext.Equals(pageContext)); + var resourceItem = _dictionary.GetPageItem(pageContext); if (resourceItem != null && resourceItem.Instance == null) { @@ -294,7 +255,7 @@ private IEndpoint CreatePageInstance(IPageContext pageContext) /// The context of the plugin whose pages are to be associated. private void Register(IPluginContext pluginContext) { - if (_dictionary.ContainsKey(pluginContext)) + if (_dictionary.Contains(pluginContext)) { return; } @@ -310,7 +271,7 @@ private void Register(IApplicationContext applicationContext) { foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) { - if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + if (_dictionary.Contains(pluginContext, applicationContext)) { continue; } @@ -449,16 +410,9 @@ public void Remove(IPluginContext pluginContext) } // the plugin has not been registered in the manager - if (_dictionary.TryGetValue(pluginContext, out var value)) + foreach (var pageContext in _dictionary.RemovePage(pluginContext)) { - foreach (var resourceItem in value.Values - .SelectMany(x => x.Values)) - { - OnRemovePage(resourceItem.PageContext); - resourceItem.Dispose(); - } - - _dictionary.Remove(pluginContext); + OnRemovePage(pageContext); } } @@ -473,18 +427,9 @@ internal void Remove(IApplicationContext applicationContext) return; } - foreach (var pluginDict in _dictionary.Values) + foreach (var pageContext in _dictionary.RemovePage(applicationContext)) { - foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) - { - foreach (var resourceItem in appDict.Values) - { - OnRemovePage(resourceItem.PageContext); - resourceItem.Dispose(); - } - } - - pluginDict.Remove(applicationContext); + OnRemovePage(pageContext); } } From 4cf648c52a8ff0970c40bb48dfae1bf0e58483cf Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 2 Feb 2025 21:49:03 +0100 Subject: [PATCH 129/162] general improvements and bug fixes --- .../WebAsset/AssetContext.cs | 1 - .../WebAttribute/ApplicationAttribute.cs | 4 +- .../WebAttribute/ISegmentAttribute.cs | 3 ++ .../WebAttribute/IdAttribute.cs | 3 +- .../WebAttribute/JobAttribute.cs | 4 ++ .../WebAttribute/ParentAttribute.cs | 6 ++- .../WebAttribute/SegmentDoubleAttribute.cs | 4 ++ .../WebAttribute/SegmentGuidAttribute.cs | 6 +-- .../WebAttribute/SegmentIntAttribute.cs | 4 ++ .../WebAttribute/SegmentStringAttribute.cs | 4 ++ .../WebAttribute/SegmentUIntAttribute.cs | 7 ++++ .../WebAttribute/StatusResponseAttribute.cs | 6 ++- .../WebAttribute/TitleAttribute.cs | 5 ++- .../WebComponent/ComponentActivator.cs | 17 +++++--- .../WebComponent/Model/ComponentItem.cs | 3 ++ .../WebEndpoint/EndpointManager.cs | 6 +-- .../WebEvent/Model/EventDictionary.cs | 14 ++++--- src/WebExpress.WebCore/WebEx.cs | 2 +- .../WebFragment/FragmentComparer.cs | 6 ++- .../WebFragment/IFragmentDynamic.cs | 9 ++++- src/WebExpress.WebCore/WebHtml/Css.cs | 3 ++ src/WebExpress.WebCore/WebHtml/Favicon.cs | 4 +- .../WebHtml/HtmlAttribute.cs | 5 ++- .../WebHtml/HtmlAttributeNoneValue.cs | 2 +- src/WebExpress.WebCore/WebHtml/HtmlComment.cs | 4 +- src/WebExpress.WebCore/WebHtml/HtmlElement.cs | 12 ++++-- .../WebHtml/HtmlElementEmbeddedParam.cs | 3 +- .../WebHtml/HtmlElementEmbeddedSource.cs | 2 +- .../WebHtml/HtmlElementFieldLabel.cs | 6 ++- .../WebHtml/HtmlElementFieldLegend.cs | 2 +- .../WebHtml/HtmlElementFieldSelect.cs | 6 ++- .../WebHtml/HtmlElementFormOptgroup.cs | 2 +- .../WebHtml/HtmlElementFormOption.cs | 10 +++-- .../WebHtml/HtmlElementInteractiveSummary.cs | 2 +- .../WebHtml/HtmlElementMultimediaArea.cs | 3 +- .../WebHtml/HtmlElementMultimediaMap.cs | 2 +- .../WebHtml/HtmlElementMultimediaTrack.cs | 2 +- .../WebHtml/HtmlElementSectionBody.cs | 4 +- .../WebHtml/HtmlElementSectionMain.cs | 2 +- .../WebHtml/HtmlElementTextContentDd.cs | 2 +- .../WebHtml/HtmlElementTextContentDt.cs | 2 +- .../WebHtml/HtmlElementTextSemanticsQ.cs | 2 +- .../WebHtml/HtmlElementTextSemanticsRp.cs | 2 +- src/WebExpress.WebCore/WebHtml/HtmlList.cs | 1 - src/WebExpress.WebCore/WebHtml/HtmlRaw.cs | 7 +++- src/WebExpress.WebCore/WebHtml/IHtml.cs | 3 ++ .../WebHtml/IHtmlAttribute.cs | 5 ++- .../WebHtml/IHtmlElementFormItem.cs | 3 ++ src/WebExpress.WebCore/WebHtml/Style.cs | 3 ++ src/WebExpress.WebCore/WebHtml/TypeEnctype.cs | 7 +++- src/WebExpress.WebCore/WebHtml/TypeFavicon.cs | 22 ++++++++++ src/WebExpress.WebCore/WebHtml/TypeTarget.cs | 29 ++++++++++++++ .../WebIdentity/IIdentityManager.cs | 19 +++++---- .../Model/IdentityPermissionDictionary.cs | 16 ++++---- .../Model/IdentityRoleDictionary.cs | 16 ++++---- src/WebExpress.WebCore/WebJob/Clock.cs | 34 ++++++++-------- src/WebExpress.WebCore/WebJob/Cron.cs | 6 ++- .../WebJob/Model/ScheduleDictionary.cs | 14 ++++--- src/WebExpress.WebCore/WebLog/ILog.cs | 18 ++++----- src/WebExpress.WebCore/WebLog/Log.cs | 22 +++++----- src/WebExpress.WebCore/WebLog/LogFactory.cs | 7 +++- src/WebExpress.WebCore/WebLog/LogFrame.cs | 3 +- .../WebLog/LogFrameSimple.cs | 3 +- src/WebExpress.WebCore/WebLog/LogItem.cs | 7 ++-- .../WebMessage/HttpContext.cs | 7 ++-- .../WebMessage/HttpExceptionContext.cs | 5 ++- .../WebMessage/ParameterFile.cs | 3 ++ src/WebExpress.WebCore/WebMessage/Request.cs | 20 ---------- .../WebMessage/RequestAuthorization.cs | 17 ++++++-- .../WebMessage/RequestMethod.cs | 40 +++++++++++++++++-- .../WebMessage/ResponseBadRequest.cs | 2 +- .../WebMessage/ResponseForbidden.cs | 2 +- .../WebMessage/ResponseInternalServerError.cs | 2 +- .../WebMessage/ResponseMovedPermanently.cs | 2 +- .../WebMessage/ResponseNotFound.cs | 2 +- .../WebMessage/ResponseUnauthorized.cs | 2 +- .../WebPackage/Model/PackageCatalog.cs | 7 +++- .../WebPackage/Model/PackageCatalogItem.cs | 5 ++- .../Model/PackageCatalogeItemState.cs | 9 +++-- .../WebPackage/Model/PackageItem.cs | 3 ++ .../WebPage/Model/PageDictionary.cs | 2 +- src/WebExpress.WebCore/WebPage/Page.cs | 8 ++-- src/WebExpress.WebCore/WebPage/PageManager.cs | 9 ++++- .../WebPage/RedirectException.cs | 11 +++-- .../WebPage/VisualTreeContext.cs | 2 +- .../WebPlugin/IPluginManager.cs | 2 +- .../WebPlugin/PluginContext.cs | 5 ++- .../WebResource/Model/ResourceDictionary.cs | 14 ++++--- .../WebResource/Model/ResourceItem.cs | 7 ---- .../WebResource/ResourceAsset.cs | 7 ++-- .../WebResource/ResourceManager.cs | 9 ++++- src/WebExpress.WebCore/WebRestAPI/IRestApi.cs | 1 - .../WebRestAPI/Model/RestApiDictionary.cs | 14 ++++--- .../WebRestAPI/RestApiManager.cs | 17 +++++--- .../Model/SessionPropertyParameter.cs | 2 +- .../Model/SettingPageDictionaryItemSection.cs | 5 ++- .../WebSettingPage/SettingPageManager.cs | 9 ++++- .../WebSitemap/ISitemapManager.cs | 15 ++++--- .../WebStatusPage/StatusPageManager.cs | 1 + .../WebUri/UriPathSegmentVariableDouble.cs | 4 +- .../WebUri/UriPathSegmentVariableGuid.cs | 19 +++++++-- .../WebUri/UriPathSegmentVariableInt.cs | 2 +- .../WebUri/UriPathSegmentVariableString.cs | 4 +- .../WebUri/UriPathSegmentVariableUInt.cs | 4 +- src/WebExpress.WebCore/WebUri/UriQuerry.cs | 2 +- src/WebExpress.WebCore/WebUri/UriResource.cs | 22 +++++----- src/WebExpress.WebCore/WebUri/UriScheme.cs | 27 +++++++++++++ 107 files changed, 538 insertions(+), 257 deletions(-) diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs index 2a450e0..7961173 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetContext.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -64,7 +64,6 @@ public class AssetContext : IAssetContext /// /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. /// - /// The endpoint manager responsible for managing endpoints. /// The context path of the resource. /// The path segment of the resource. public AssetContext(UriResource contextPath, IUriPathSegment pathSegment) diff --git a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs index 5c67ab7..adb5ee8 100644 --- a/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ApplicationAttribute.cs @@ -6,9 +6,9 @@ namespace WebExpress.WebCore.WebAttribute /// /// An application expression attribute, which is determined by the type. /// - /// The type of the application. + /// The type of the application. [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class ApplicationAttribute : Attribute, IPluginAttribute where T : class, IApplication + public class ApplicationAttribute : Attribute, IPluginAttribute where TApplication : class, IApplication { } diff --git a/src/WebExpress.WebCore/WebAttribute/ISegmentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ISegmentAttribute.cs index 9022a55..10ac119 100644 --- a/src/WebExpress.WebCore/WebAttribute/ISegmentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ISegmentAttribute.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Interface for converting an object to a URI path segment. + /// public interface ISegmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs index d42542a..2a0cb44 100644 --- a/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IdAttribute.cs @@ -3,12 +3,13 @@ /// /// The unique identification key. /// + [System.AttributeUsage(System.AttributeTargets.Class)] public class IdAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute, IEndpointAttribute { /// /// Initializes a new instance of the class. /// - /// The id. + /// The id. public IdAttribute(string id) { diff --git a/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs b/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs index e8c8ca5..e6fbe3e 100644 --- a/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/JobAttribute.cs @@ -1,5 +1,9 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Represents an attribute to schedule jobs based on specified time intervals. + /// + [System.AttributeUsage(System.AttributeTargets.Class)] public class JobAttribute : System.Attribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs index 434d12e..fbaa428 100644 --- a/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs @@ -3,8 +3,12 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to specify the parent endpoint for a given endpoint. + /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ParentAttribute : Attribute, IEndpointAttribute where T : class, IEndpoint + public class ParentAttribute : Attribute, IEndpointAttribute + where TEndpoint : class, IEndpoint { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs index 9e3bf3b..1c2a208 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentDoubleAttribute.cs @@ -3,6 +3,10 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to define a double segment in a URI path. + /// + [AttributeUsage(AttributeTargets.Class)] public class SegmentDoubleAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs index 7c13d3f..57d1d7d 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentGuidAttribute.cs @@ -8,7 +8,8 @@ namespace WebExpress.WebCore.WebAttribute /// A dynamic path segment of type guid. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SegmentGuidAttribute : Attribute, IEndpointAttribute, ISegmentAttribute where T : Parameter + public class SegmentGuidAttribute : Attribute, IEndpointAttribute, ISegmentAttribute + where TParameter : Parameter { /// /// Returns or sets the name of the variable. @@ -28,12 +29,11 @@ public class SegmentGuidAttribute : Attribute, IEndpointAttribute, ISegmentAt /// /// Initializes a new instance of the class. /// - /// The type of the variable. /// The display string. /// The display format. public SegmentGuidAttribute(string display, UriPathSegmentVariableGuid.Format displayFormat = UriPathSegmentVariableGuid.Format.Simple) { - VariableName = (Activator.CreateInstance(typeof(T)) as Parameter)?.Key?.ToLower(); + VariableName = (Activator.CreateInstance() as Parameter)?.Key?.ToLower(); Display = display; DisplayFormat = displayFormat; } diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs index 04dd4d9..ed888a2 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentIntAttribute.cs @@ -3,6 +3,10 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to define an integer segment in a URI path. + /// + [AttributeUsage(AttributeTargets.Class)] public class SegmentIntAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs index e3440d7..d125473 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentStringAttribute.cs @@ -3,6 +3,10 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to define a segment string in a URI path. + /// + [AttributeUsage(AttributeTargets.Class)] public class SegmentStringAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs index a6b2292..fc6b841 100644 --- a/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SegmentUIntAttribute.cs @@ -3,6 +3,13 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Attribute to define a segment with an unsigned integer variable in the URI path. + /// + /// + /// This attribute is used to specify a segment in the URI path that contains an unsigned integer variable. + /// + [AttributeUsage(AttributeTargets.Class)] public class SegmentUIntAttribute : Attribute, IEndpointAttribute, ISegmentAttribute { /// diff --git a/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs b/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs index 298278c..27220cb 100644 --- a/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/StatusResponseAttribute.cs @@ -3,11 +3,13 @@ namespace WebExpress.WebCore.WebAttribute { + /// /// Specifies the status code for a starus page. /// - /// The type of the response. + /// The type of the response. [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class StatusResponseAttribute : Attribute, IStatusPageAttribute where T : Response, new() + public class StatusResponseAttribute : Attribute, IStatusPageAttribute + where TResponse : Response, new() { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs index 2145557..0fdf584 100644 --- a/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/TitleAttribute.cs @@ -1,5 +1,9 @@ namespace WebExpress.WebCore.WebAttribute { + /// + /// Represents an attribute that assigns a title to a page, setting page, or status page. + /// + [System.AttributeUsage(System.AttributeTargets.Class)] public class TitleAttribute : System.Attribute, IPageAttribute, ISettingPageAttribute, IStatusPageAttribute { /// @@ -8,7 +12,6 @@ public class TitleAttribute : System.Attribute, IPageAttribute, ISettingPageAttr /// The display text. public TitleAttribute(string display) { - } } } diff --git a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs index ff33f76..ed44dfd 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentActivator.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Reflection; +using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebStatusPage; @@ -255,7 +256,13 @@ public static TComponent CreateInstance(Type componentType hubProperties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? .GetValue(componentHub) ?? - advancedParameters.Where(x => x.GetType() == parameter.ParameterType) + advancedParameters.Where(x => + x.GetType() == parameter.ParameterType || + ( + parameter.ParameterType == typeof(IApplicationContext) && + x.GetType().GetInterfaces().Any(x => x == typeof(IApplicationContext)) + ) + ) .FirstOrDefault() ?? null ).ToArray(); @@ -272,15 +279,15 @@ public static TComponent CreateInstance(Type componentType /// /// Creates an instance of the specified component type with the provided context and component hub and advanced parameters. /// - /// The type of the component, which must implement . - /// The type of the context, which must implement . + /// The type of the context, which must implement . /// The type of the component to create. /// The context to pass to the component's constructor. /// The reference to the context of the host. /// The component hub to use for dependency injection. /// Additional parameters to pass to the component's constructor. /// An instance of the specified component type. - public static IComponent CreateInstance(Type componentType, C context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) where C : IContext + public static IComponent CreateInstance(Type componentType, TContext context, IHttpServerContext httpServerContext, IComponentHub componentHub, params object[] advancedParameters) + where TContext : IContext { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var constructors = componentType?.GetConstructors(flags); @@ -299,7 +306,7 @@ public static IComponent CreateInstance(Type componentType, C context, IHttpS var parameterValues = parameters.Select(parameter => parameter.ParameterType == typeof(IComponentHub) ? componentHub : parameter.ParameterType == typeof(IHttpServerContext) ? httpServerContext : - parameter.ParameterType == typeof(C) ? context : + parameter.ParameterType == typeof(TContext) ? context : parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(context) : properties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? diff --git a/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs b/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs index 1c85b30..a3df3b5 100644 --- a/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs +++ b/src/WebExpress.WebCore/WebComponent/Model/ComponentItem.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebComponent.Model { + /// + /// Represents an item of a web component, including its class type, ID, and instance. + /// public class ComponentItem { /// diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index ca85b7e..2d6d95f 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -14,7 +14,7 @@ namespace WebExpress.WebCore.WebEndpoint /// public sealed class EndpointManager : IEndpointManager, ISystemComponent { - private readonly IComponentHub _componentHub; + //private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly Dictionary _registrations = []; @@ -41,7 +41,7 @@ public sealed class EndpointManager : IEndpointManager, ISystemComponent [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] private EndpointManager(IComponentHub componentHub, IHttpServerContext httpServerContext) { - _componentHub = componentHub; + //_componentHub = componentHub; _httpServerContext = httpServerContext; @@ -81,7 +81,7 @@ public void Remove() where TEndpointContext : IEndpointContext endpointRegistration.RemoveEndpoint -= OnRemoveEndpoint; } - //// + /// /// Returns an enumeration of endpoint contexts. /// /// The endpoint type. diff --git a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs index 7c51b29..49c2412 100644 --- a/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs +++ b/src/WebExpress.WebCore/WebEvent/Model/EventDictionary.cs @@ -60,9 +60,10 @@ public bool AddEventItem(IPluginContext pluginContext, IApplicationContext appli /// /// The plugin context. /// The application context. - public void RemoveEventHandler(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IEvent + public void RemoveEventHandler(IPluginContext pluginContext, IApplicationContext applicationContext) + where TEvent : IEvent { - var type = typeof(T); + var type = typeof(TEvent); if (ContainsKey(pluginContext)) { @@ -93,19 +94,20 @@ public void RemoveEventHandler(IPluginContext pluginContext, IApplicationCont /// /// Returns the event handler from the dictionary. /// - /// The type of event. + /// The type of event. /// The application context. /// An IEnumerable of event items - public IEnumerable GetEventHandlerItems(IApplicationContext applicationContext) where T : IEvent + public IEnumerable GetEventHandlerItems(IApplicationContext applicationContext) + where TEvent : IEvent { - return GetEventHandlerItems(applicationContext, typeof(T)); + return GetEventHandlerItems(applicationContext, typeof(TEvent)); } /// /// Returns the event handler from the dictionary. /// /// The application context. - /// The type of event. + /// The type of event. /// An IEnumerable of event items public IEnumerable GetEventHandlerItems(IApplicationContext applicationContext, Type eventType) { diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 38a530b..572d9a7 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -144,7 +144,7 @@ private void OnCancel(object sender, ConsoleCancelEventArgs e) /// Initialization /// /// The valid arguments. - /// The configuration file. + /// The configuration file. private void Initialization(string args, string configFile) { // Config laden diff --git a/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs b/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs index 57a5939..2d627ff 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentComparer.cs @@ -4,7 +4,11 @@ namespace WebExpress.WebCore.WebFragment { - [Obsolete] + /// + /// Provides a method to compare two objects of type T for equality. + /// + /// The type of objects to compare. + [Obsolete("FragmentComparer is obsolete. Use a different comparer implementation.")] public class FragmentComparer : IEqualityComparer { /// diff --git a/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs b/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs index f86754e..6a16ce4 100644 --- a/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs +++ b/src/WebExpress.WebCore/WebFragment/IFragmentDynamic.cs @@ -4,10 +4,14 @@ namespace WebExpress.WebCore.WebFragment { + /// + /// Interface representing a dynamic fragment. + /// Provides methods for initialization and creation of fragments. + /// public interface IFragmentDynamic { /// - /// Returns the context of the fragment.. + /// Returns the context of the fragment. /// IFragmentContext Context { get; } @@ -22,6 +26,7 @@ public interface IFragmentDynamic /// Creates fragments of a common type T. /// /// The created instances of the fragments. - IEnumerable Create() where T : IComponent; + IEnumerable Create() + where TComponent : IComponent; } } diff --git a/src/WebExpress.WebCore/WebHtml/Css.cs b/src/WebExpress.WebCore/WebHtml/Css.cs index c69ef69..0b385f1 100644 --- a/src/WebExpress.WebCore/WebHtml/Css.cs +++ b/src/WebExpress.WebCore/WebHtml/Css.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Provides utility methods for working with CSS classes. + /// public static class Css { /// diff --git a/src/WebExpress.WebCore/WebHtml/Favicon.cs b/src/WebExpress.WebCore/WebHtml/Favicon.cs index 3b8964d..1498868 100644 --- a/src/WebExpress.WebCore/WebHtml/Favicon.cs +++ b/src/WebExpress.WebCore/WebHtml/Favicon.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents a favicon with a URL and media type. + /// public class Favicon { /// @@ -27,7 +30,6 @@ public Favicon(string url, TypeFavicon mediatype) /// Initializes a new instance of the class. /// /// The uri. - /// The media type. public Favicon(string url) { Url = url; diff --git a/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs b/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs index 1c0b8e8..21057d1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlAttribute.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents an HTML attribute that can be added to an HTML element. + /// public class HtmlAttribute : IHtmlAttribute { /// @@ -52,7 +55,7 @@ public virtual void ToString(StringBuilder builder, int deep) builder.Append(Name); builder.Append("=\""); builder.Append(Value); - builder.Append("\""); + builder.Append('"'); } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs b/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs index 33c9419..a586231 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlAttributeNoneValue.cs @@ -4,7 +4,7 @@ namespace WebExpress.WebCore.WebHtml { /// /// An attribute without value. - /// e.g. required in input + /// e.g. required in input required /// public class HtmlAttributeNoneValue : IHtmlAttribute { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlComment.cs b/src/WebExpress.WebCore/WebHtml/HtmlComment.cs index 9b1cc8a..66a63f7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlComment.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlComment.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents an HTML comment node. + /// public class HtmlComment : IHtmlNode { /// @@ -14,7 +17,6 @@ public class HtmlComment : IHtmlNode /// public HtmlComment() { - } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs index f964abf..141c3de 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElement.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElement.cs @@ -105,17 +105,23 @@ public string OnClick /// /// Initializes a new instance of the class. /// - /// The name of the item. + /// The name of the HTML element. + /// A boolean value indicating whether the element requires a closing tag. Default is true. public HtmlElement(string name, bool closeTag = true) { + ElementName = name; + CloseTag = closeTag; - } + + } /// /// Initializes a new instance of the class. /// - /// The name of the item. + /// The name of the HTML element. + /// A boolean value indicating whether the element requires a closing tag. + /// An array of IHtml nodes to be added to the element. public HtmlElement(string name, bool closeTag, params IHtml[] nodes) : this(name, closeTag) { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs index d012882..32f066e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedParam.cs @@ -2,7 +2,7 @@ { /// /// Represents a parameter for a plugin that can be used for the display - /// of an embedded element. + /// of an object embedded element. /// public class HtmlElementEmbeddedParam : HtmlElement, IHtmlElementEmbedded { @@ -12,7 +12,6 @@ public class HtmlElementEmbeddedParam : HtmlElement, IHtmlElementEmbedded public HtmlElementEmbeddedParam() : base("param", false) { - } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs index 7bfaab8..7460214 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementEmbeddedSource.cs @@ -2,7 +2,7 @@ { /// /// Allows authors to specify alternative media resources (e.g., different audio or video - /// formats) for media elements such as public class HtmlElementEmbeddedSource : HtmlElement, IHtmlElementEmbedded { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs index 5b8c75d..7f4e656 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLabel.cs @@ -5,9 +5,11 @@ namespace WebExpress.WebCore.WebHtml { /// /// Represents the label for a form control element (e.g. text input fields). - /// - /// /// + /// + /// + /// + /// public class HtmlElementFieldLabel : HtmlElement, IHtmlElementFormItem { /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs index ac02d69..a496130 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldLegend.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents a label for an
element. + /// Represents a label for an fieldset element. ///
public class HtmlElementFieldLegend : HtmlElement { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs index 13e7d6d..e089175 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFieldSelect.cs @@ -5,11 +5,13 @@ namespace WebExpress.WebCore.WebHtml { /// /// Represents a control that can be used to select from a range of options. + /// + /// /// - ///
+ /// public class HtmlElementFieldSelect : HtmlElement, IHtmlElementFormItem { /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs index b4ade2f..4050bae 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOptgroup.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.WebHtml /// /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs index ca8b3c6..78c988e 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementFormOption.cs @@ -4,12 +4,14 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents a selection option within an - /// - /// + /// + /// /// - /// + /// public class HtmlElementFormOption : HtmlElement, IHtmlElementFormItem { /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs index 54bf29f..b6ddb7d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementInteractiveSummary.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents a summary or legend for a specific
element. + /// Represents a summary or legend for a specific details element. ///
public class HtmlElementInteractiveSummary : HtmlElement, IHtmlElementInteractive { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs index 99d5030..34519e7 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaArea.cs @@ -1,7 +1,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents an image map in conjunction with the element. + /// Represents an image map in conjunction with the map element. /// public class HtmlElementMultimediaArea : HtmlElement, IHtmlElementMultimedia { @@ -11,7 +11,6 @@ public class HtmlElementMultimediaArea : HtmlElement, IHtmlElementMultimedia public HtmlElementMultimediaArea() : base("area", false) { - } } } diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs index bc6247c..4075919 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaMap.cs @@ -1,7 +1,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents an image map in conjunction with the element. + /// Represents an image map in conjunction with the area element. /// public class HtmlElementMultimediaMap : HtmlElement, IHtmlElementMultimedia { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs index e20362c..99908fc 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementMultimediaTrack.cs @@ -1,7 +1,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Allows you to specify additional media tracks (e.g. subtitles) for elements such as public class HtmlElementMultimediaTrack : HtmlElement, IHtmlElementMultimedia { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs index 0ee49c0..08ecf45 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionBody.cs @@ -5,7 +5,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents the main content of an HTML document. Each document can contain only one element. + /// Represents the main content of an HTML document. Each document can contain only one body element. /// public class HtmlElementSectionBody : HtmlElement, IHtmlElementSection { @@ -49,7 +49,7 @@ public List ScriptLinks public HtmlElementSectionBody() : base("body") { - ElementScriptLinks = new List(); + ElementScriptLinks = []; } /// diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs index c0e092a..07c9575 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementSectionMain.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents the main content of the page. Only one
element per page is allowed. + /// Represents the main content of the page. Only one main element per page is allowed. ///
public class HtmlElementSectionMain : HtmlElement, IHtmlElementSection { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs index 9732031..99d6990 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDd.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents the definition of the term(s) specified in the immediately preceding
element. + /// Represents the definition of the term(s) specified in the immediately preceding dt element. ///
public class HtmlElementTextContentDd : HtmlElement, IHtmlElementTextContent { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs index 6669fcc..b199b8d 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextContentDt.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents a term described in the following
element. + /// Represents a term described in the following dt element. ///
public class HtmlElementTextContentDt : HtmlElement, IHtmlElementTextContent { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs index 0df39f7..16d580a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsQ.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Represents a short quote. For longer quotes should
be used. + /// Represents a short quote. For longer quotes should blockquote be used. ///
public class HtmlElementTextSemanticsQ : HtmlElement, IHtmlElementTextSemantics { diff --git a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs index 0e3a5d8..78cc839 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlElementTextSemanticsRp.cs @@ -4,7 +4,7 @@ namespace WebExpress.WebCore.WebHtml { /// - /// Used along with the element to surround Ruby text with parentheses that + /// Used along with the element ruby to surround Ruby text with parentheses that /// appear when the user program (browser) does not support Ruby annotations. /// public class HtmlElementTextSemanticsRp : HtmlElement, IHtmlElementTextSemantics diff --git a/src/WebExpress.WebCore/WebHtml/HtmlList.cs b/src/WebExpress.WebCore/WebHtml/HtmlList.cs index 365a73c..29144a1 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlList.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlList.cs @@ -80,7 +80,6 @@ protected void Add(params IHtmlNode[] elements) ///
/// The string builder. /// The call depth. - /// Start the closing tag on a new line. public void ToString(StringBuilder builder, int deep) { foreach (var v in _elements) diff --git a/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs b/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs index 0a45db8..184496a 100644 --- a/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs +++ b/src/WebExpress.WebCore/WebHtml/HtmlRaw.cs @@ -2,6 +2,12 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents a raw HTML node. + /// + /// + /// This class is used to encapsulate raw HTML content. + /// public class HtmlRaw : IHtmlNode { /// @@ -14,7 +20,6 @@ public class HtmlRaw : IHtmlNode /// public HtmlRaw() { - } /// diff --git a/src/WebExpress.WebCore/WebHtml/IHtml.cs b/src/WebExpress.WebCore/WebHtml/IHtml.cs index da9ce08..be25f43 100644 --- a/src/WebExpress.WebCore/WebHtml/IHtml.cs +++ b/src/WebExpress.WebCore/WebHtml/IHtml.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Interface for HTML elements. + /// public interface IHtml { /// diff --git a/src/WebExpress.WebCore/WebHtml/IHtmlAttribute.cs b/src/WebExpress.WebCore/WebHtml/IHtmlAttribute.cs index 8a54ec3..4f8b2af 100644 --- a/src/WebExpress.WebCore/WebHtml/IHtmlAttribute.cs +++ b/src/WebExpress.WebCore/WebHtml/IHtmlAttribute.cs @@ -1,9 +1,12 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents an HTML attribute that can be added to an HTML element. + /// public interface IHtmlAttribute : IHtml { /// - /// Returns or sets the name. des Attributes + /// Returns or sets the attribute name. /// string Name { get; set; } diff --git a/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs b/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs index e18dad3..755e5a9 100644 --- a/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs +++ b/src/WebExpress.WebCore/WebHtml/IHtmlElementFormItem.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents an element that is part of a form item. + /// public interface IHtmlElementFormItem : IHtmlElementForm { } diff --git a/src/WebExpress.WebCore/WebHtml/Style.cs b/src/WebExpress.WebCore/WebHtml/Style.cs index 3ee465a..27d6e02 100644 --- a/src/WebExpress.WebCore/WebHtml/Style.cs +++ b/src/WebExpress.WebCore/WebHtml/Style.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Represents utility methods for managing CSS styles. + /// public static class Style { /// diff --git a/src/WebExpress.WebCore/WebHtml/TypeEnctype.cs b/src/WebExpress.WebCore/WebHtml/TypeEnctype.cs index 18146b4..8fad32e 100644 --- a/src/WebExpress.WebCore/WebHtml/TypeEnctype.cs +++ b/src/WebExpress.WebCore/WebHtml/TypeEnctype.cs @@ -9,20 +9,26 @@ public enum TypeEnctype /// All characters are encoded (spaces are conferred to "+" and special characters in the hex representation). /// UrLEncoded, + /// /// No characters will be encodes. Used when transferring files. /// None, + /// /// Only space characters are encoded. /// Text, + /// /// Not assignable. /// Default } + /// + /// Provides extension methods for the TypeEnctype enumeration. + /// public static class TypeEnctypeExtensions { /// @@ -41,7 +47,6 @@ public static TypeEnctype Convert(string enctype) }; } - /// /// Conversion to string.repräsentation /// diff --git a/src/WebExpress.WebCore/WebHtml/TypeFavicon.cs b/src/WebExpress.WebCore/WebHtml/TypeFavicon.cs index 8a689c6..f0c855b 100644 --- a/src/WebExpress.WebCore/WebHtml/TypeFavicon.cs +++ b/src/WebExpress.WebCore/WebHtml/TypeFavicon.cs @@ -1,11 +1,33 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Enumeration representing the different types of favicons. + /// public enum TypeFavicon { + /// + /// The default favicon type. + /// Default, + + /// + /// Favicon type for .ico files. + /// ICON, + + /// + /// Favicon type for .png files. + /// PNG, + + /// + /// Favicon type for .jpg files. + /// JPG, + + /// + /// Favicon type for .svg files. + /// SVG } } diff --git a/src/WebExpress.WebCore/WebHtml/TypeTarget.cs b/src/WebExpress.WebCore/WebHtml/TypeTarget.cs index 25e5b67..ae56f13 100644 --- a/src/WebExpress.WebCore/WebHtml/TypeTarget.cs +++ b/src/WebExpress.WebCore/WebHtml/TypeTarget.cs @@ -1,15 +1,44 @@ namespace WebExpress.WebCore.WebHtml { + /// + /// Specifies the target for hyperlinks or forms. + /// public enum TypeTarget { + /// + /// No target specified. + /// None, + + /// + /// Opens the link in a new window or tab. + /// Blank, + + /// + /// Opens the link in the same frame as it was clicked. + /// Self, + + /// + /// Opens the link in the parent frame. + /// Parent, + + /// + /// Opens the link in the full body of the window. + /// Top, + + /// + /// Opens the link in a named frame. + /// Framename } + /// + /// Provides extension methods for the TypeTarget enum. + /// public static class TypeTargetExtensions { /// diff --git a/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs b/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs index af1a5e0..34c29be 100644 --- a/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs +++ b/src/WebExpress.WebCore/WebIdentity/IIdentityManager.cs @@ -57,11 +57,12 @@ public interface IIdentityManager : IComponentManager /// /// Checks if the specified identity has the given permission. /// - /// The type of the identity permission. + /// The type of the identity permission. /// The context of the application. /// The identity to check. /// True if the identity has the permission, false otherwise. - bool CheckAccess(IApplicationContext applicationContext, IIdentity identity) where T : IIdentityPermission; + bool CheckAccess(IApplicationContext applicationContext, IIdentity identity) + where TIdentityPermission : IIdentityPermission; /// /// Checks if the specified identity has the given permission. @@ -75,11 +76,12 @@ public interface IIdentityManager : IComponentManager /// /// Checks if the specified identity group has the given permission. /// - /// The type of the identity permission. + /// The type of the identity permission. /// The context of the application. /// The identity group to check. /// True if the identity group has the permission, false otherwise. - bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group) where T : IIdentityPermission; + bool CheckAccess(IApplicationContext applicationContext, IIdentityGroup group) + where TIdentityPermission : IIdentityPermission; /// /// Checks if the specified identity group has the given permission. @@ -93,15 +95,18 @@ public interface IIdentityManager : IComponentManager /// /// Checks if the specified identity role has the given permission. /// - /// The type of the identity role. - /// The type of the identity permission. + /// The type of the identity role. + /// The type of the identity permission. /// The context of the application. /// True if the identity role has the permission, false otherwise. - bool CheckAccess(IApplicationContext applicationContext) where R : IIdentityRole where P : IIdentityPermission; + bool CheckAccess(IApplicationContext applicationContext) + where TIdentityRole : IIdentityRole + where TIdentityPermission : IIdentityPermission; /// /// Checks if the specified identity role has the given permission. /// + /// The context of the application. /// The identity role to check. /// The permission to check for. /// True if the identity role has the permission, false otherwise. diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs index ec759c1..3ddc71f 100644 --- a/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityPermissionDictionary.cs @@ -54,9 +54,10 @@ public bool AddPermissionItem(IPluginContext pluginContext, IApplicationContext /// /// The plugin context. /// The application context. - public void RemovePermissionItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IIdentityPermission + public void RemovePermissionItem(IPluginContext pluginContext, IApplicationContext applicationContext) + where TIdentityPermission : IIdentityPermission { - var type = typeof(T); + var type = typeof(TIdentityPermission); if (ContainsKey(pluginContext)) { @@ -88,25 +89,26 @@ public void RemovePermissionItem(IPluginContext pluginContext, IApplicationCo /// /// Returns the permission items from the dictionary. /// - /// The type of the permission. + /// The type of the permission. /// The application context. /// An IEnumerable of permission items - public IEnumerable GetPermissionItems(IApplicationContext applicationContext) where T : IIdentityPermission + public IEnumerable GetPermissionItems(IApplicationContext applicationContext) + where TIdentityPermission : IIdentityPermission { - return GetPermissionItems(applicationContext, typeof(T)); + return GetPermissionItems(applicationContext, typeof(TIdentityPermission)); } /// /// Returns the permission items from the dictionary. /// /// The application context. - /// The type of the permission. + /// The type of the permission. /// An IEnumerable of permission items public IEnumerable GetPermissionItems(IApplicationContext applicationContext, Type permissionType) { if (!typeof(IIdentityPermission).IsAssignableFrom(permissionType)) { - return Enumerable.Empty(); + return []; } if (ContainsKey(applicationContext?.PluginContext)) diff --git a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs index 48b55fe..8097df8 100644 --- a/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs +++ b/src/WebExpress.WebCore/WebIdentity/Model/IdentityRoleDictionary.cs @@ -29,7 +29,7 @@ public bool AddRoleItem(IPluginContext pluginContext, IApplicationContext applic if (!TryGetValue(pluginContext, out var appContextDict)) { - appContextDict = new Dictionary>(); + appContextDict = []; this[pluginContext] = appContextDict; } @@ -54,9 +54,10 @@ public bool AddRoleItem(IPluginContext pluginContext, IApplicationContext applic /// /// The plugin context. /// The application context. - public void RemoveRoleItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IIdentityRole + public void RemoveRoleItem(IPluginContext pluginContext, IApplicationContext applicationContext) + where TIdentityRole : IIdentityRole { - var type = typeof(T); + var type = typeof(TIdentityRole); if (ContainsKey(pluginContext)) { @@ -88,19 +89,20 @@ public void RemoveRoleItem(IPluginContext pluginContext, IApplicationContext /// /// Returns the role items from the dictionary. /// - /// The type of the role. + /// The type of the role. /// The application context. /// An IEnumerable of role items - public IEnumerable GetRoleItems(IApplicationContext applicationContext) where T : IIdentityRole + public IEnumerable GetRoleItems(IApplicationContext applicationContext) + where TIdentityRole : IIdentityRole { - return GetRoleItems(applicationContext, typeof(T)); + return GetRoleItems(applicationContext, typeof(TIdentityRole)); } /// /// Returns the role items from the dictionary. /// /// The application context. - /// The type of the role. + /// The type of the role. /// An IEnumerable of role items public IEnumerable GetRoleItems(IApplicationContext applicationContext, Type roleType) { diff --git a/src/WebExpress.WebCore/WebJob/Clock.cs b/src/WebExpress.WebCore/WebJob/Clock.cs index a35d5ca..a607027 100644 --- a/src/WebExpress.WebCore/WebJob/Clock.cs +++ b/src/WebExpress.WebCore/WebJob/Clock.cs @@ -3,37 +3,37 @@ namespace WebExpress.WebCore.WebJob { + /// + /// Represents a clock that provides time-related properties and methods. + /// public class Clock { - /// - /// The underlying date and time. - /// - private DateTime DateTime { get; set; } + private DateTime _dateTime; /// /// The minute 0-59. /// - public int Minute => DateTime.Minute; + public int Minute => _dateTime.Minute; /// /// The hour 0-23. /// - public int Hour => DateTime.Hour; + public int Hour => _dateTime.Hour; /// /// The day 1-31. /// - public int Day => DateTime.Day; + public int Day => _dateTime.Day; /// /// The month 1-12. /// - public int Month => DateTime.Month; + public int Month => _dateTime.Month; /// /// The weekday 0-6 (Sunday-Saturday). /// - public int Weekday => (int)DateTime.DayOfWeek; + public int Weekday => (int)_dateTime.DayOfWeek; /// /// Initializes a new instance of the class. @@ -42,7 +42,7 @@ public Clock() { var dateTime = DateTime.Now; - DateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); + _dateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); } /// @@ -51,7 +51,7 @@ public Clock() /// The time to copy. public Clock(DateTime dateTime) { - DateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); + _dateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); } /// @@ -60,7 +60,7 @@ public Clock(DateTime dateTime) /// The clock to be copied. public Clock(Clock clock) { - DateTime = clock.DateTime; + _dateTime = clock._dateTime; } /// @@ -68,7 +68,7 @@ public Clock(Clock clock) /// internal void Tick() { - DateTime = DateTime.AddMinutes(1); + _dateTime = _dateTime.AddMinutes(1); } /// @@ -89,7 +89,7 @@ public IEnumerable Synchronize() next.Tick(); } - DateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); + _dateTime = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, dateTime.Minute, 0); return elapsed; } @@ -124,7 +124,7 @@ public IEnumerable Synchronize() /// True wenn die linke Uhrzeit kleiner ist als die Rechte, false sonst public static bool operator <(Clock obj1, Clock obj2) { - return obj1.DateTime < obj2.DateTime; + return obj1._dateTime < obj2._dateTime; } /// @@ -135,7 +135,7 @@ public IEnumerable Synchronize() /// True if the time on the left is greater than the time on the right, false otherwise. public static bool operator >(Clock obj1, Clock obj2) { - return obj1.DateTime > obj2.DateTime; + return obj1._dateTime > obj2._dateTime; } /// @@ -189,7 +189,7 @@ public override bool Equals(object obj) /// The hash code. public override int GetHashCode() { - return DateTime.GetHashCode(); + return _dateTime.GetHashCode(); } } } diff --git a/src/WebExpress.WebCore/WebJob/Cron.cs b/src/WebExpress.WebCore/WebJob/Cron.cs index 3e2ad4b..2118480 100644 --- a/src/WebExpress.WebCore/WebJob/Cron.cs +++ b/src/WebExpress.WebCore/WebJob/Cron.cs @@ -8,7 +8,9 @@ namespace WebExpress.WebCore.WebJob /// /// Manages commands that are executed on a scheduled basis (CRON = command run on notice). /// - /// + /// + /// For more information, see Cron. + /// public class Cron { /// @@ -118,7 +120,7 @@ private static IEnumerable Parse(string value, int minValue, int maxValue) { if (result >= minValue && result <= maxValue) { - items = items.Union(new List { result }); + items = items.Union([result]); } else { diff --git a/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs index f481d0e..fb2298a 100644 --- a/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs +++ b/src/WebExpress.WebCore/WebJob/Model/ScheduleDictionary.cs @@ -66,9 +66,10 @@ public bool AddScheduleItem(IPluginContext pluginContext, IApplicationContext ap /// /// The plugin context. /// The application context. - public void RemoveScheduleItem(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IEvent + public void RemoveScheduleItem(IPluginContext pluginContext, IApplicationContext applicationContext) + where TEvent : IEvent { - var type = typeof(T); + var type = typeof(TEvent); if (ContainsKey(pluginContext)) { @@ -99,19 +100,20 @@ public void RemoveScheduleItem(IPluginContext pluginContext, IApplicationCont /// /// Returns the schedule items from the dictionary. /// - /// The type of event. + /// The type of event. /// The application context. /// An IEnumerable of schedule items - public IEnumerable GetScheduleItems(IApplicationContext applicationContext) where T : IJob + public IEnumerable GetScheduleItems(IApplicationContext applicationContext) + where TJob : IJob { - return GetScheduleItems(applicationContext, typeof(T)); + return GetScheduleItems(applicationContext, typeof(TJob)); } /// /// Returns the schedule items from the dictionary. /// /// The application context. - /// The type of job. + /// The type of job. /// An IEnumerable of event items public IEnumerable GetScheduleItems(IApplicationContext applicationContext, Type jobType) { diff --git a/src/WebExpress.WebCore/WebLog/ILog.cs b/src/WebExpress.WebCore/WebLog/ILog.cs index 427634e..71ff833 100644 --- a/src/WebExpress.WebCore/WebLog/ILog.cs +++ b/src/WebExpress.WebCore/WebLog/ILog.cs @@ -18,15 +18,15 @@ namespace WebExpress.WebCore.WebLog /// column defines the level of the log entry. The third column lists the function that produced the entry. /// The last column indicates a note or error description. /// - /// - /// Example:
- /// 08:26:30 Info Program.Main Startup
- /// 08:26:30 Info Program.Main --------------------------------------------------
- /// 08:26:30 Info Program.Main Version: 0.0.0.1
- /// 08:26:30 Info Program.Main Arguments: -test
- /// 08:26:30 Info Program.Main Configuration version: V1
- /// 08:26:30 Info Program.Main Processing: sequentiell
- ///
+ /// + /// Example: + /// 08:26:30 Info Program.Main Startup + /// 08:26:30 Info Program.Main -------------------------------------------------- + /// 08:26:30 Info Program.Main Version: 0.0.0.1 + /// 08:26:30 Info Program.Main Arguments: -test + /// 08:26:30 Info Program.Main Configuration version: V1 + /// 08:26:30 Info Program.Main Processing: sequentiell + /// public interface ILog : ILogger { /// diff --git a/src/WebExpress.WebCore/WebLog/Log.cs b/src/WebExpress.WebCore/WebLog/Log.cs index 2c99439..b437925 100644 --- a/src/WebExpress.WebCore/WebLog/Log.cs +++ b/src/WebExpress.WebCore/WebLog/Log.cs @@ -22,15 +22,15 @@ namespace WebExpress.WebCore.WebLog /// column defines the level of the log entry. The third column lists the function that produced the entry. /// The last column indicates a note or error description. /// - /// - /// Example:
- /// 08:26:30 Info Program.Main Startup
- /// 08:26:30 Info Program.Main --------------------------------------------------
- /// 08:26:30 Info Program.Main Version: 0.0.0.1
- /// 08:26:30 Info Program.Main Arguments: -test
- /// 08:26:30 Info Program.Main Configuration version: V1
- /// 08:26:30 Info Program.Main Processing: sequentiell
- ///
+ /// + /// Example: + /// 08:26:30 Info Program.Main Startup + /// 08:26:30 Info Program.Main -------------------------------------------------- + /// 08:26:30 Info Program.Main Version: 0.0.0.1 + /// 08:26:30 Info Program.Main Arguments: -test + /// 08:26:30 Info Program.Main Configuration version: V1 + /// 08:26:30 Info Program.Main Processing: sequentiell + /// public class Log : ILog { /// @@ -116,7 +116,7 @@ public class Log : ILog /// /// Unsaved entries queue. /// - private readonly Queue _queue = new Queue(); + private readonly Queue _queue = new(); /// /// Initializes a new instance of the class. @@ -185,7 +185,7 @@ public void Begin(string path) public void Begin(SettingLogItem settings) { Filename = settings.Filename; - LogMode = (LogMode)Enum.Parse(typeof(LogMode), settings.Modus); + LogMode = Enum.Parse(settings.Modus); Encoding = Encoding.GetEncoding(settings.Encoding); TimePattern = settings.Timepattern; DebugMode = settings.Debug; diff --git a/src/WebExpress.WebCore/WebLog/LogFactory.cs b/src/WebExpress.WebCore/WebLog/LogFactory.cs index 3b531f5..87507af 100644 --- a/src/WebExpress.WebCore/WebLog/LogFactory.cs +++ b/src/WebExpress.WebCore/WebLog/LogFactory.cs @@ -1,7 +1,11 @@ using Microsoft.Extensions.Logging; +using System; namespace WebExpress.WebCore.WebLog { + /// + /// Provides a factory for creating loggers and adding logger providers. + /// public class LogFactory : ILoggerFactory, ILoggerProvider { /// @@ -10,7 +14,6 @@ public class LogFactory : ILoggerFactory, ILoggerProvider /// The ILoggerProvider. public void AddProvider(ILoggerProvider provider) { - } /// @@ -30,7 +33,7 @@ public ILogger CreateLogger(string categoryName) /// public void Dispose() { - + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebLog/LogFrame.cs b/src/WebExpress.WebCore/WebLog/LogFrame.cs index 130e24d..d564b12 100644 --- a/src/WebExpress.WebCore/WebLog/LogFrame.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrame.cs @@ -69,12 +69,11 @@ public LogFrame(string name, string additionalHeading = null, [CallerMemberName] /// /// Release unmanaged resources that were reserved during initialization. /// - /// The input data. - /// The output data. public virtual void Dispose() { Log.Info("".PadRight(80, '='), Instance, Line, File); Log.Info(Status, Instance, Line, File); + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs index d2153a4..2cca5d8 100644 --- a/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs +++ b/src/WebExpress.WebCore/WebLog/LogFrameSimple.cs @@ -48,11 +48,10 @@ public LogFrameSimple(ILog log, [CallerMemberName] string instance = null, [Call /// /// Release unmanaged resources that were reserved during initialization. /// - /// The input data. - /// The output data. public virtual void Dispose() { Log.Info("".PadRight(80, '<'), Instance, Line, File); + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebLog/LogItem.cs b/src/WebExpress.WebCore/WebLog/LogItem.cs index cc45ef1..2891a74 100644 --- a/src/WebExpress.WebCore/WebLog/LogItem.cs +++ b/src/WebExpress.WebCore/WebLog/LogItem.cs @@ -30,9 +30,10 @@ internal class LogItem /// /// Initializes a new instance of the class. /// - /// The level. - /// The modul/funktion. + /// The level of the log entry. + /// The module or function where the log entry originated. /// The log message. + /// The time pattern used for formatting the timestamp. public LogItem(LogLevel level, string instance, string message, string timePattern) { m_level = level; @@ -50,7 +51,7 @@ public override string ToString() { if (m_level != LogLevel.Seperartor) { - return m_timestamp.ToString(TimePattern) + " " + m_level.ToString().PadRight(9, ' ') + " " + m_instance.PadRight(19, ' ').Substring(0, 19) + " " + m_message; + return m_timestamp.ToString(TimePattern) + " " + m_level.ToString().PadRight(9, ' ') + " " + m_instance.PadRight(19, ' ')[..19] + " " + m_message; } else { diff --git a/src/WebExpress.WebCore/WebMessage/HttpContext.cs b/src/WebExpress.WebCore/WebMessage/HttpContext.cs index e621a4c..b76dbf9 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpContext.cs @@ -1,11 +1,13 @@ using Microsoft.AspNetCore.Http.Features; using System; -using System.Linq; using System.Net; using System.Text; namespace WebExpress.WebCore.WebMessage { + /// + /// Represents the context of an HTTP request and response. + /// public class HttpContext { /// @@ -53,7 +55,6 @@ public class HttpContext /// internal HttpContext() { - } /// @@ -73,7 +74,7 @@ public HttpContext(IFeatureCollection contextFeatures, IHttpServerContext httpSe LocalEndPoint = new IPEndPoint(connectionFeature.LocalIpAddress, connectionFeature.LocalPort); RemoteEndPoint = new IPEndPoint(connectionFeature.RemoteIpAddress, connectionFeature.RemotePort); - Encoding = requestFeature.Headers.ContentEncoding.Any() ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; + Encoding = requestFeature.Headers.ContentEncoding.Count != 0 ? Encoding.GetEncoding(requestFeature.Headers.ContentEncoding) : Encoding.Default; Uri = new Uri(baseUri, requestFeature.RawTarget); Request = new Request(contextFeatures, header, httpServerContext); diff --git a/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs b/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs index c2ff313..e4aa046 100644 --- a/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs +++ b/src/WebExpress.WebCore/WebMessage/HttpExceptionContext.cs @@ -4,6 +4,9 @@ namespace WebExpress.WebCore.WebMessage { + /// + /// Represents the context for an HTTP exception, inheriting from . + /// public class HttpExceptionContext : HttpContext { /// @@ -19,7 +22,7 @@ public class HttpExceptionContext : HttpContext public HttpExceptionContext(Exception exception, IFeatureCollection contextFeatures) { var connectionFeature = contextFeatures.Get(); - var requestFeature = contextFeatures.Get(); + //var requestFeature = contextFeatures.Get(); Features = contextFeatures; Id = connectionFeature.ConnectionId; diff --git a/src/WebExpress.WebCore/WebMessage/ParameterFile.cs b/src/WebExpress.WebCore/WebMessage/ParameterFile.cs index 233a991..492396b 100644 --- a/src/WebExpress.WebCore/WebMessage/ParameterFile.cs +++ b/src/WebExpress.WebCore/WebMessage/ParameterFile.cs @@ -1,5 +1,8 @@ namespace WebExpress.WebCore.WebMessage { + /// + /// Represents a file parameter with content type and data. + /// public class ParameterFile : Parameter { /// diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index 0bbfb6a..3f9333e 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -65,31 +65,11 @@ public class Request /// public EndPoint RemoteEndPoint { get; private set; } - /// - /// Returns a boolean value that indicates whether the client sending this request is authenticated. - /// - //public bool IsAuthenticated { get; private set; } //=> RawRequuest.IsAuthenticated; - - /// - /// Returns a boolean value that indicates whether the request was sent from the local computer. - /// - //public bool IsLocal { get; private set; } //=> RawRequuest.IsLocal; - /// /// Returns a boolean value that indicates whether the tcp connection used to send the request uses the secure sockets layer (ssl) protocol. /// public bool IsSecureConnection { get; private set; } - /// - /// Returns a boolean value indicating whether the tcp connection was a web socket request. - /// - //public bool IsWebSocketRequest { get; private set; } // => RawRequuest.IsWebSocketRequest; - - /// - /// Returns a boolean value that indicates whether the client is requesting a persistent connection. - /// - //public bool KeepAlive { get; private set; } //=> RawRequuest.KeepAlive; - /// /// Returns the shema. This can be http or https. /// diff --git a/src/WebExpress.WebCore/WebMessage/RequestAuthorization.cs b/src/WebExpress.WebCore/WebMessage/RequestAuthorization.cs index 1983f03..428f21d 100644 --- a/src/WebExpress.WebCore/WebMessage/RequestAuthorization.cs +++ b/src/WebExpress.WebCore/WebMessage/RequestAuthorization.cs @@ -1,11 +1,20 @@ using System; -using System.Linq; using System.Text.RegularExpressions; namespace WebExpress.WebCore.WebMessage { - public class RequestAuthorization + /// + /// Represents an authorization request containing type, identification, and password. + /// + public partial class RequestAuthorization { + /// + /// Returns a regular expression to match the authorization header. + /// + /// A object for matching authorization headers. + [GeneratedRegex("^(.*) (.*)$")] + private static partial Regex AuthorizationRegex(); + /// /// Returns or sets the type.e (Basic bei WWW-Authenticate: Basic realm="RealmName") /// @@ -33,7 +42,7 @@ public static RequestAuthorization Parse(string str) return null; } - var m = Regex.Match(str, "^(.*) (.*)$"); + var m = AuthorizationRegex().Match(str); var type = "Basic"; var user = ""; var password = ""; @@ -47,7 +56,7 @@ public static RequestAuthorization Parse(string str) var split = userPw.Split(':'); user = split[0]; - password = split.Count() > 0 ? split[1] : ""; + password = split.Length > 0 ? split[1] : ""; } return new RequestAuthorization() diff --git a/src/WebExpress.WebCore/WebMessage/RequestMethod.cs b/src/WebExpress.WebCore/WebMessage/RequestMethod.cs index 1f8abbd..9e55e71 100644 --- a/src/WebExpress.WebCore/WebMessage/RequestMethod.cs +++ b/src/WebExpress.WebCore/WebMessage/RequestMethod.cs @@ -1,23 +1,57 @@ namespace WebExpress.WebCore.WebMessage { + /// + /// Enumeration of HTTP request methods. + /// public enum RequestMethod { + /// + /// No request method specified. + /// NONE, + + /// + /// The GET method requests a representation of the specified resource. + /// GET, + + /// + /// The POST method submits an entity to the specified resource. + /// POST, + + /// + /// The PUT method replaces all current representations of the target resource with the request payload. + /// PUT, + + /// + /// The HEAD method asks for a response identical to a GET request, but without the response body. + /// HEAD, + + /// + /// The DELETE method deletes the specified resource. + /// DELETE, + + /// + /// The PATCH method applies partial modifications to a resource. + /// PATCH // RFC 5789 } + /// + /// Provides extension methods for the enumeration. + /// public static class RequestMethodExtensions { + /// - /// Umwandlung in eine CSS-Klasse + /// Converts the enumeration value to its string representation. /// - /// Das Layout, welches umgewandelt werden soll - /// Die zum Layout gehörende CSS-KLasse + /// The enumeration value. + /// A string representation of the enumeration value. public static string ToString(this RequestMethod layout) { return layout switch diff --git a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs index f8b785e..1032f9e 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseBadRequest.cs @@ -20,7 +20,7 @@ public ResponseBadRequest() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseBadRequest(StatusMessage message) { var content = message?.Message ?? "404404 - Bad Request"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs index 3466864..f47f220 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseForbidden.cs @@ -20,7 +20,7 @@ public ResponseForbidden() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseForbidden(StatusMessage message) { var content = message?.Message ?? "403403 - Forbidden"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs index 258c7e1..a0c05e9 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseInternalServerError.cs @@ -20,7 +20,7 @@ public ResponseInternalServerError() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseInternalServerError(StatusMessage message) { var content = message?.Message ?? "404500 - Internal Server Error"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs b/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs index 76ed162..a5ac961 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseMovedPermanently.cs @@ -21,7 +21,7 @@ public ResponseMovedPermanently() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseMovedPermanently(StatusMessage message) { var content = message?.Message ?? "404301 - Moved Permanently"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs index 863e81c..43ba686 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseNotFound.cs @@ -21,7 +21,7 @@ public ResponseNotFound() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseNotFound(StatusMessage message) { var content = message?.Message ?? "404404 - Not Found"; diff --git a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs index 2efe4d9..bef3d01 100644 --- a/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs +++ b/src/WebExpress.WebCore/WebMessage/ResponseUnauthorized.cs @@ -21,7 +21,7 @@ public ResponseUnauthorized() /// /// Initializes a new instance of the class. /// - /// The user defined status message or null. + /// The user defined status message or null. public ResponseUnauthorized(StatusMessage message) { Reason = "OK"; diff --git a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs index 9ab98ab..05bab46 100644 --- a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalog.cs @@ -5,6 +5,9 @@ namespace WebExpress.WebCore.WebPackage.Model { + /// + /// Represents a catalog of packages. + /// [XmlRoot("catalog")] public class PackageCatalog { @@ -12,13 +15,13 @@ public class PackageCatalog /// Returns the package entries in the catalog. /// [XmlElement("package")] - public List Packages { get; } = new List(); + public List Packages { get; } = []; /// /// Returns the system package entries in the catalog. /// [XmlIgnore] - public List SystemPackages { get; } = new List(); + public List SystemPackages { get; } = []; /// /// Locates a specific catalog item. diff --git a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs index b87f156..c4d3b54 100644 --- a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogItem.cs @@ -4,6 +4,9 @@ namespace WebExpress.WebCore.WebPackage.Model { + /// + /// Represents an item in the package catalog. + /// [XmlRoot("package")] public class PackageCatalogItem { @@ -29,7 +32,7 @@ public class PackageCatalogItem /// Returns the plugins belonging to the package. /// [XmlIgnore] - public List Plugins { get; internal set; } = new List(); + public List Plugins { get; internal set; } = []; /// /// Returns the meta information about the package. diff --git a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs index 46d1725..0caf616 100644 --- a/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageCatalogeItemState.cs @@ -1,19 +1,22 @@ namespace WebExpress.WebCore.WebPackage.Model { + /// + /// Represents the state of a package in the catalog. + /// public enum PackageCatalogeItemState { /// - /// Das Paket ist verfügbar, jedoch noch nicht vom WebExpress geladen. + /// The package is available but has not yet been loaded by WebExpress. /// Available, /// - /// Das Paket wurde geladen und steht zur Nutzung bereit. + /// The package has been loaded and is ready for use. /// Active, /// - /// Das Paket wurde deaktiviert. Die Nutzung des Paketes ist nicht möglich. + /// The package has been disabled. The use of the package is not possible. /// Disable } diff --git a/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs index c33a97c..5df0709 100644 --- a/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs +++ b/src/WebExpress.WebCore/WebPackage/Model/PackageItem.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebPackage.Model { + /// + /// Represents an item in a web package. + /// public class PackageItem { /// diff --git a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs index 207b562..2925951 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageDictionary.cs @@ -275,7 +275,7 @@ public bool Contains(IPluginContext pluginContext) return _dict.ContainsKey(pluginContext); } - // + /// /// Checks if the dictionary contains the specified plugin context and application context. /// /// The plugin context to check for. diff --git a/src/WebExpress.WebCore/WebPage/Page.cs b/src/WebExpress.WebCore/WebPage/Page.cs index 4ad7a53..db4c456 100644 --- a/src/WebExpress.WebCore/WebPage/Page.cs +++ b/src/WebExpress.WebCore/WebPage/Page.cs @@ -3,8 +3,9 @@ /// /// The prototype of a website. /// - /// An implementation of the visualization tree. - public abstract class Page : IPage where T : IVisualTree, new() + /// An implementation of the visualization tree. + public abstract class Page : IPage + where TVisualTree : IVisualTree, new() { /// /// Returns or sets the page title. @@ -19,7 +20,6 @@ /// /// Initializes a new instance of the class. /// - /// The context of the page. public Page() { } @@ -39,7 +39,7 @@ public virtual void Redirecting(string uri) /// /// The context for rendering the page. /// The visual tree to be rendered. - public abstract void Process(IRenderContext renderContext, T visualTree); + public abstract void Process(IRenderContext renderContext, TVisualTree visualTree); /// /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index ae92873..6ba2218 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -236,7 +236,14 @@ private IEndpoint CreatePageInstance(IPageContext pageContext) if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.PageClass, pageContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance + ( + resourceItem.PageClass, + pageContext, + _httpServerContext, + _componentHub, + pageContext.ApplicationContext + ); if (resourceItem.Cache) { diff --git a/src/WebExpress.WebCore/WebPage/RedirectException.cs b/src/WebExpress.WebCore/WebPage/RedirectException.cs index 9d189f4..5a34808 100644 --- a/src/WebExpress.WebCore/WebPage/RedirectException.cs +++ b/src/WebExpress.WebCore/WebPage/RedirectException.cs @@ -2,23 +2,26 @@ namespace WebExpress.WebCore.WebPage { + /// + /// Represents an exception that is thrown to redirect a web page. + /// public class RedirectException : Exception { /// - /// Liefert oder setzt das Weiterleitungsziel + /// Returns or sets the redirection target. /// public string Url { get; set; } /// - /// Bestimmt, ob ein permanete Weiterleitung erfolgen soll + /// Determines whether a permanent redirection should occur. /// public bool Permanet { get; set; } /// /// Initializes a new instance of the class. /// - /// Das Weiterleitungsziel - /// true wenn 301 gesendet werden soll, flase für 302 + /// The redirection target. + /// true if 301 should be sent, false for 302. public RedirectException(string url, bool permanent = false) : base("Redirecting to " + url) { diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs index 76f7bc8..e5ab594 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -26,7 +26,7 @@ public class VisualTreeContext : IVisualTreeContext /// /// Initializes a new instance of the class. /// - /// The context to copy./param> + /// The context to copy. public VisualTreeContext(IRenderContext context) { RenderContext = context; diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs index 5dcd747..2386890 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPluginManager.cs @@ -35,7 +35,7 @@ public interface IPluginManager : IComponentManager /// /// Returns a plugin context based on its id. /// - /// The type of the plugin. + /// The type of the plugin. /// The plugin context. IPluginContext GetPlugin(Type plugin); diff --git a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs index 1228871..1f6ef97 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs @@ -4,6 +4,9 @@ namespace WebExpress.WebCore.WebPlugin { + /// + /// Represents the context of a plugin, providing access to its metadata and host context. + /// public class PluginContext : IPluginContext { /// @@ -69,7 +72,7 @@ public PluginContext() /// The string that uniquely represents the plugin. public override string ToString() { - return $"Plugin: {PluginId.ToString()}"; + return $"Plugin: {PluginId}"; } } } diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs index 8df59ca..d0ca85d 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceDictionary.cs @@ -56,9 +56,10 @@ public bool AddResourceItem(IPluginContext pluginContext, IApplicationContext ap /// /// The plugin context. /// The application context. - public void RemoveResource(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IResource + public void RemoveResource(IPluginContext pluginContext, IApplicationContext applicationContext) + where TResource : IResource { - var type = typeof(T); + var type = typeof(TResource); if (ContainsKey(pluginContext)) { @@ -89,19 +90,20 @@ public void RemoveResource(IPluginContext pluginContext, IApplicationContext /// /// Returns the resource items from the dictionary. /// - /// The type of resource. + /// The type of resource. /// The application context. /// An IEnumerable of resource items - public IEnumerable GetResourceItems(IApplicationContext applicationContext) where T : IResource + public IEnumerable GetResourceItems(IApplicationContext applicationContext) + where TResource : IResource { - return GetResourceItems(applicationContext, typeof(T)); + return GetResourceItems(applicationContext, typeof(TResource)); } /// /// Returns the resource items from the dictionary. /// /// The application context. - /// The type of resource. + /// The type of resource. /// An IEnumerable of resource items public IEnumerable GetResourceItems(IApplicationContext applicationContext, Type resourceType) { diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index d877efa..7ca8890 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -28,13 +28,6 @@ internal class ResourceItem : IDisposable /// public IEndpoint Instance { get; set; } - /// - /// Returns the scope names that provides the resource. The scope name - /// is a string with a name (e.g. global, admin), which can be used by elements to - /// determine whether content and how content should be displayed. - /// - //public IReadOnlyList Scopes { get; set; } - /// /// Returns or sets the paths of the resource. /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index 4a938cd..301db97 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -131,9 +131,10 @@ public override Response Process(Request request) /// /// Reads the data of a specified resource. /// - /// The file. - /// The assembly. - /// The data. + /// The name of the resource file to read. + /// The assembly containing the resource. + /// A collection of resource names available in the assembly. + /// A byte array containing the resource data, or null if the resource is not found. private static byte[] GetData(string file, Assembly assembly, IEnumerable resources) { var item = resources.Where(x => x.Equals(file, System.StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 06f3fd7..d6d6d8b 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -375,7 +375,14 @@ private IResource CreateResourceInstance(IResourceContext resourceContext) if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.ResourceClass, resourceContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance + ( + resourceItem.ResourceClass, + resourceContext, + _httpServerContext, + _componentHub, + resourceContext.ApplicationContext + ); if (resourceItem.Cache) { diff --git a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs index 97684e1..df36426 100644 --- a/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs +++ b/src/WebExpress.WebCore/WebRestAPI/IRestApi.cs @@ -30,7 +30,6 @@ public interface IRestApi : IEndpoint /// /// Deletes data. /// - /// The id of the data to delete. /// The request. void DeleteData(Request request); } diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs index b3e202f..f1fac46 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiDictionary.cs @@ -56,9 +56,10 @@ public bool AddRestApiItem(IPluginContext pluginContext, IApplicationContext app /// /// The plugin context. /// The application context. - public void RemoveRestApi(IPluginContext pluginContext, IApplicationContext applicationContext) where T : IRestApi + public void RemoveRestApi(IPluginContext pluginContext, IApplicationContext applicationContext) + where TRestApi : IRestApi { - var type = typeof(T); + var type = typeof(TRestApi); if (ContainsKey(pluginContext)) { @@ -89,19 +90,20 @@ public void RemoveRestApi(IPluginContext pluginContext, IApplicationContext a /// /// Returns the rest api items from the dictionary. /// - /// The type of rest api. + /// The type of rest api. /// The application context. /// An IEnumerable of rest api items - public IEnumerable GetRestApiItems(IApplicationContext applicationContext) where T : IRestApi + public IEnumerable GetRestApiItems(IApplicationContext applicationContext) + where TRestApi : IRestApi { - return GetRestApiItems(applicationContext, typeof(T)); + return GetRestApiItems(applicationContext, typeof(TRestApi)); } /// /// Returns the rest api items from the dictionary. /// /// The application context. - /// The type of rest api. + /// The type of rest api. /// An IEnumerable of rest api items public IEnumerable GetRestApiItems(IApplicationContext applicationContext, Type restApiType) { diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 3042159..3c5ffbe 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -67,7 +67,7 @@ private RestApiManager(IComponentHub componentHub, IHttpServerContext httpServer HandleRequest = (request, endpointContext) => { var restApiContext = endpointContext as IRestApiContext; - var restApi = CreatePageInstance(restApiContext) as IRestApi; + var restApi = CreateApiInstance(restApiContext) as IRestApi; if (restApiContext.Methods.Any(x => x.Equals((CrudMethod)request.Method))) { @@ -232,18 +232,25 @@ public IRestApiContext GetRestApi(string applicationId, string restApiId) /// /// Creates a new rest api resource and returns it. If a rest api resource already exists (through caching), the existing instance is returned. /// - /// The context used for rest api resource creation. + /// The context used for rest api resource creation. /// The created or cached rest api resource. - private IRestApi CreatePageInstance(IRestApiContext pageContext) + private IRestApi CreateApiInstance(IRestApiContext apiContext) { var resourceItem = _dictionary.Values .SelectMany(x => x.Values) .SelectMany(x => x.Values) - .FirstOrDefault(x => x.RestApiContext.Equals(pageContext)); + .FirstOrDefault(x => x.RestApiContext.Equals(apiContext)); if (resourceItem != null && resourceItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(resourceItem.RestApiClass, pageContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance + ( + resourceItem.RestApiClass, + apiContext, + _httpServerContext, + _componentHub, + apiContext.ApplicationContext + ); if (resourceItem.Cache) { diff --git a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs index 6080c9d..e1a01d1 100644 --- a/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs +++ b/src/WebExpress.WebCore/WebSession/Model/SessionPropertyParameter.cs @@ -23,7 +23,7 @@ public SessionPropertyParameter() /// /// Initializes a new instance of the class. /// - /// The parameters + /// The parameters. public SessionPropertyParameter(params Parameter[] parameters) { foreach (var param in parameters) diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs index f165ce5..e5f2c0d 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs @@ -2,6 +2,9 @@ namespace WebExpress.WebCore.WebSettingPage.Model { + /// + /// Represents a dictionary that maps setting sections to their corresponding groups of setting page items. + /// public class SettingPageDictionaryItemSection : Dictionary { /// @@ -16,7 +19,7 @@ public bool AddSettingPageItem(SettingSection section, string group, SettingPage // register Section if (!ContainsKey(section)) { - Add(section, new SettingPageDictionaryItemGroup()); + Add(section, []); } return this[section].AddSettingPageItem(group, item); diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index d77e339..b34ce2d 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -164,7 +164,14 @@ private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageConte if (settingPageItem != null && settingPageItem.Instance == null) { - var instance = ComponentActivator.CreateInstance(settingPageItem.SettingPageClass, settingPageContext, _httpServerContext, _componentHub); + var instance = ComponentActivator.CreateInstance + ( + settingPageItem.SettingPageClass, + settingPageContext, + _httpServerContext, + _componentHub, + settingPageContext.ApplicationContext + ); if (settingPageItem.Cache) { diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs index 863ec2e..681d2a6 100644 --- a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -34,10 +34,11 @@ public interface ISitemapManager : IComponentManager /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). /// /// Returns the uri taking into account the context or null. - UriResource GetUri(params Parameter[] parameters) where T : IEndpoint; + UriResource GetUri(params Parameter[] parameters) + where TEndpoint : IEndpoint; /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. @@ -50,18 +51,20 @@ public interface ISitemapManager : IComponentManager /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). /// The application context. /// Returns the uri taking into account the context or null. - UriResource GetUri(IApplicationContext applicationContext) where T : IEndpoint; + UriResource GetUri(IApplicationContext applicationContext) + where TEndpoint : IEndpoint; /// /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). /// The endpoint context. /// Returns the uri taking into account the context or null. - UriResource GetUri(IEndpointContext endpointContext) where T : IEndpoint; + UriResource GetUri(IEndpointContext endpointContext) + where TEndpoint : IEndpoint; /// /// Retrieves the endpoint context associated with the given URI. diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index d114d7f..9c6ffb2 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -342,6 +342,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon parameter.ParameterType == typeof(IComponentHub) ? _componentHub : parameter.ParameterType == typeof(IHttpServerContext) ? _httpServerContext : parameter.ParameterType == typeof(IPageContext) ? pageContext : + parameter.ParameterType == typeof(IApplicationContext) ? pageContext?.ApplicationContext : parameter.ParameterType == typeof(IComponentId) ? contextIdProperty?.GetValue(pageContext) : hubProperties.Where(x => x.PropertyType == parameter.ParameterType) .FirstOrDefault()? diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs index 2536c72..d165294 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableDouble.cs @@ -10,7 +10,7 @@ public class UriPathSegmentVariableDouble : UriPathSegmentVariable /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The tag or null public UriPathSegmentVariableDouble(string name, object tag = null) : base(name, tag) @@ -25,7 +25,7 @@ public UriPathSegmentVariableDouble(string name, object tag = null) /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The tag or null public UriPathSegmentVariableDouble(string name, string display, object tag = null) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs index ee8af60..0b536f9 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableGuid.cs @@ -6,14 +6,25 @@ namespace WebExpress.WebCore.WebUri { /// - /// Variable path segment. + /// Represents a URI path segment variable for GUIDs. /// public class UriPathSegmentVariableGuid : UriPathSegmentVariable { /// /// The display formats of the guid. /// - public enum Format { Full, Simple } + public enum Format + { + /// + /// Full format of the guid. + /// + Full, + + /// + /// Simple format of the guid. + /// + Simple + } /// /// Returns the display format. @@ -33,7 +44,7 @@ public UriPathSegmentVariableGuid(string name, object tag = null) /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The tag or null public UriPathSegmentVariableGuid(string name, string display, object tag = null) @@ -44,7 +55,7 @@ public UriPathSegmentVariableGuid(string name, string display, object tag = null /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The display format. /// The tag or null diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs index 2d28a5b..a2a3b04 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableInt.cs @@ -25,7 +25,7 @@ public UriPathSegmentVariableInt(string name, object tag = null) /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The tag or null public UriPathSegmentVariableInt(string name, string display, object tag = null) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs index 29b4825..ba56c3f 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableString.cs @@ -10,7 +10,7 @@ public class UriPathSegmentVariableString : UriPathSegmentVariable /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The tag or null public UriPathSegmentVariableString(string name, object tag = null) : base(name, tag) @@ -25,7 +25,7 @@ public UriPathSegmentVariableString(string name, object tag = null) /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The tag or null public UriPathSegmentVariableString(string name, string display, object tag = null) diff --git a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs index 40e2145..b7ad4c7 100644 --- a/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs +++ b/src/WebExpress.WebCore/WebUri/UriPathSegmentVariableUInt.cs @@ -10,7 +10,7 @@ public class UriPathSegmentVariableUInt : UriPathSegmentVariable /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The tag or null public UriPathSegmentVariableUInt(string name, object tag = null) : base(name, tag) @@ -25,7 +25,7 @@ public UriPathSegmentVariableUInt(string name, object tag = null) /// /// Initializes a new instance of the class. /// - /// The path text. + /// The name. /// The display text. /// The tag or null public UriPathSegmentVariableUInt(string name, string display, object tag = null) diff --git a/src/WebExpress.WebCore/WebUri/UriQuerry.cs b/src/WebExpress.WebCore/WebUri/UriQuerry.cs index 598984b..8ed3979 100644 --- a/src/WebExpress.WebCore/WebUri/UriQuerry.cs +++ b/src/WebExpress.WebCore/WebUri/UriQuerry.cs @@ -1,7 +1,7 @@ namespace WebExpress.WebCore.WebUri { /// - /// The query part (e.g. ?title=Uniform_Resource_Identifier&action=submit). + /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// public class UriQuerry { diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriResource.cs index 0cf6a6a..3b4acea 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriResource.cs @@ -51,7 +51,7 @@ public UriResource ExtendedPath } /// - /// The query part (e.g. ?title=Uniform_Resource_Identifier&action=submit). + /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// public ICollection Query { get; } = []; @@ -162,7 +162,7 @@ public UriResource(UriScheme scheme, UriAuthority authority, string uri) /// /// Initializes a new instance of the class. /// - /// The uri. + /// The uri. public UriResource(string uri) { if (string.IsNullOrWhiteSpace(uri) || uri == "/") return; @@ -273,7 +273,7 @@ public UriResource(UriResource uri, IEnumerable segments, IEnum /// The scheme (e.g. Http, FTP). /// The authority (e.g. user@example.com:8080). /// References a position within a resource (e.g. #Anchor). - /// The query part (e.g. ?title=Uniform_Resource_Identifier&action=submit). + /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// The path segments. public UriResource(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) { @@ -333,12 +333,12 @@ public virtual UriResource Append(IUriPathSegment path) /// /// Return a shortened uri containing n-elements. - /// count > 0 count elements are included - /// count < 0 count elements are truncated + /// count greater than 0 count elements are included + /// count less than 0 count elements are truncated /// count = 0 an empty uri is returned /// - /// The count. - /// The sub uri. + /// The count of elements to include or truncate. + /// The sub uri with the specified number of elements. public virtual UriResource Take(int count) { var copy = new UriResource(this); @@ -367,11 +367,11 @@ public virtual UriResource Take(int count) /// /// Return a shortened uri by not including the first n elements. - /// count > 0 count elements are skipped - /// count <= 0 an empty Uri is returned + /// count greater than 0 count elements are skipped + /// count less than or equals 0 an empty Uri is returned /// - /// The count. - /// The sub uri. + /// The count of elements to skip. + /// The sub uri after skipping the specified number of elements. public UriResource Skip(int count) { if (count >= PathSegments.Count) diff --git a/src/WebExpress.WebCore/WebUri/UriScheme.cs b/src/WebExpress.WebCore/WebUri/UriScheme.cs index 661a592..7937edc 100644 --- a/src/WebExpress.WebCore/WebUri/UriScheme.cs +++ b/src/WebExpress.WebCore/WebUri/UriScheme.cs @@ -5,12 +5,39 @@ namespace WebExpress.WebCore.WebUri /// public enum UriScheme { + /// + /// The File URI scheme. + /// File, + + /// + /// The FTP URI scheme. + /// FTP, + + /// + /// The HTTP URI scheme. + /// Http, + + /// + /// The HTTPS URI scheme. + /// Https, + + /// + /// The LDAP URI scheme. + /// Ldap, + + /// + /// The LDAPS URI scheme. + /// Ldaps, + + /// + /// The Mailto URI scheme. + /// Mailto } } \ No newline at end of file From caf693d561b99fa7c153f279db3d69ffc2fbc428 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Thu, 6 Mar 2025 16:07:04 +0100 Subject: [PATCH 130/162] feat: update and enhance the settings page manager --- .../WebAttribute/SettingCategoryAttribute.cs | 20 ++ .../WebAttribute/SettingContextAttribute.cs | 20 -- .../WebPage/IPageContext.cs | 5 + src/WebExpress.WebCore/WebPage/PageContext.cs | 5 + src/WebExpress.WebCore/WebPage/PageManager.cs | 6 +- .../WebSettingPage/ISettingPageContext.cs | 5 + .../WebSettingPage/ISettingPageManager.cs | 40 ++++ .../Model/SettingPageDictionary.cs | 223 +++++++++++++++++- ...s => SettingPageDictionaryItemCategory.cs} | 6 +- .../WebSettingPage/SettingPageContext.cs | 4 +- .../WebSettingPage/SettingPageManager.cs | 171 ++++++++------ 11 files changed, 395 insertions(+), 110 deletions(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs delete mode 100644 src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs rename src/WebExpress.WebCore/WebSettingPage/Model/{SettingPageDictionaryItemContext.cs => SettingPageDictionaryItemCategory.cs} (88%) diff --git a/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs new file mode 100644 index 0000000..19665c1 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify the category in which the settings page is associated. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class SettingCategoryAttribute : Attribute, IEndpointAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The category in which the settings page is associated. + public SettingCategoryAttribute(string category) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs deleted file mode 100644 index ad4a732..0000000 --- a/src/WebExpress.WebCore/WebAttribute/SettingContextAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Attribute to specify the context in which the settings page is associated. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SettingContextAttribute : Attribute, IEndpointAttribute - { - /// - /// Initializes a new instance of the class. - /// - /// The context in which the settings page is associated. - public SettingContextAttribute(string context) - { - - } - } -} diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index 12e91b0..99a9f1b 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -20,5 +20,10 @@ public interface IPageContext : IEndpointContext /// determine whether content and how content should be displayed. /// IEnumerable Scopes { get; } + + /// + /// Returns the attributes associated with the page. + /// + IEnumerable Attributes { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index ce5753c..f270b6f 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -68,6 +68,11 @@ public class PageContext : IPageContext /// public bool IncludeSubPaths { get; internal set; } + /// + /// Returns the attributes associated with the page. + /// + public IEnumerable Attributes { get; internal set; } + /// /// Returns the context path. /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 6ba2218..c62888a 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -309,6 +309,9 @@ private void Register(IPluginContext pluginContext, IEnumerable(); var conditions = new List(); var cache = false; + var attributes = resourceType.CustomAttributes + .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && + !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) @@ -370,7 +373,8 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType) }; var pageItem = new PageItem(_componentHub.PageManager) diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index 8f8a8c5..72438da 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -14,6 +14,11 @@ public interface ISettingPageContext : IPageContext /// string Group { get; } + /// + /// Returns the setting category. + /// + string Category { get; } + /// /// Returns the section to which the setting page belongs. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs index deb38d3..f783095 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs @@ -39,5 +39,45 @@ public interface ISettingPageManager : IComponentManager /// The context of the application. /// An enumeration of setting page contextes. IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext); + + /// + /// Returns an enumeration of setting page contexts for the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// An enumeration of setting page contexts. + IEnumerable GetSettingPages(IApplicationContext applicationContext, string category); + + /// + /// Returns an enumeration of setting page contexts for the specified application context, category, and group. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// The group for which to retrieve setting pages. + /// An enumeration of setting page contexts. + IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group); + + /// + /// Returns the first setting page context for the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// The first setting page context or null. + ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, string category); + + /// + /// Returns the categories associated with the specified application context. + /// + /// The context of the application. + /// An enumeration of category names. + IEnumerable GetCategories(IApplicationContext applicationContext); + + /// + /// Returns the groups associated with the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve groups. + /// An enumeration of group names. + IEnumerable GetGroups(IApplicationContext applicationContext, string category); } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs index 2dfd507..dae2692 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs @@ -1,19 +1,24 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebSettingPage.Model { /// - /// Represents a dictionary that maps plugin contexts to application contexts and their corresponding setting page dictionary item contexts. + /// Represents a dictionary for managing setting pages, organized by plugin and application contexts. /// - internal class SettingPageDictionary : Dictionary> + internal class SettingPageDictionary { + private readonly Dictionary> _dict = []; + /// /// Returns the collection of setting pages. /// - public IEnumerable SettingPages => Values + public IEnumerable All => _dict.Values .SelectMany(a => a.Values) .SelectMany(c => c.Values) .SelectMany(s => s.Values) @@ -28,10 +33,10 @@ internal class SettingPageDictionary : DictionaryTrue if the settings page was added successfully; otherwise, false. public bool AddSettingPageItem(SettingPageItem item) { - if (!TryGetValue(item.PluginContext, out var appDict)) + if (!_dict.TryGetValue(item.PluginContext, out var appDict)) { appDict = []; - Add(item.PluginContext, appDict); + _dict.Add(item.PluginContext, appDict); } if (!appDict.TryGetValue(item.ApplicationContext, out var contextDict)) @@ -42,5 +47,211 @@ public bool AddSettingPageItem(SettingPageItem item) return contextDict.AddSettingPageItem(item.Context, item.Section, item.Group, item); } + + /// + /// Removes all elemets associated with the specified plugin context. + /// + /// The context of the plugin that contains the elemets to remove. + public void Remove(IPluginContext pluginContext) + { + _dict.Remove(pluginContext); + } + + /// + /// Removes all setting pages associated with the specified application context. + /// + /// The context of the application that contains the fragments to remove. + /// An enumerable collection of setting page contexts that were removed. + public IEnumerable Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + yield break; + } + + foreach (var pluginDict in _dict.Values) + { + foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var settingPageItem in appDict.Values + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i)) + { + yield return settingPageItem.SettingPageContext; + } + } + + pluginDict.Remove(applicationContext); + } + } + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// An enumeration of setting page contextes. + public IEnumerable GetSettingPages(Type settingPageType) + { + return _dict.Values + .SelectMany(a => a.Values) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .Where(x => x.SettingPageClass.Equals(settingPageType)) + .Select(x => x.SettingPageContext); + } + + /// + /// Returns an enumeration of setting page contextes. + /// + /// The setting page type. + /// The context of the application. + /// An enumeration of setting page contextes. + public IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext) + { + return _dict.Values + .SelectMany(a => a) + .Where(a => a.Key.Equals(applicationContext)) + .Select(a => a.Value) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .Where(x => x.SettingPageClass.Equals(settingPageType)) + .Select(x => x.SettingPageContext); + } + + /// + /// Determines whether the dictionary contains the specified plugin context. + /// + /// The context of the plugin to locate in the dictionary. + /// True if the dictionary contains an element with the specified plugin context; otherwise, false. + public bool Contains(IPluginContext pluginContext) + { + return _dict.ContainsKey(pluginContext); + } + + /// + /// Determines whether the dictionary contains the specified apllication context. + /// + /// The context of the application to locate in the dictionary. + /// True if the dictionary contains an element with the specified application context; otherwise, false. + public bool Contains(IApplicationContext applicationContext) + { + return _dict.Values.Any(x => x.ContainsKey(applicationContext)); + } + + /// + /// Creates a new setting page instance or returns an existing cached instance. + /// + /// The context used for setting page creation. + /// The central management hub for components. + /// The context of the HTTP server. + /// The created or cached setting page instance. + public IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContext, IComponentHub componentHub, IHttpServerContext httpServerContext) + { + var settingPageItem = _dict.Values + .SelectMany(a => a.Values) + .SelectMany(c => c.Values) + .SelectMany(s => s.Values) + .SelectMany(g => g.Values) + .SelectMany(i => i) + .FirstOrDefault(x => x.SettingPageContext.Equals(settingPageContext)); + + if (settingPageItem != null && settingPageItem.Instance == null) + { + var instance = ComponentActivator.CreateInstance + ( + settingPageItem.SettingPageClass, + settingPageContext, + httpServerContext, + componentHub, + settingPageContext.ApplicationContext + ); + + if (settingPageItem.Cache) + { + settingPageItem.Instance = instance; + } + + return instance; + } + + return settingPageItem?.Instance; + } + + /// + /// Returns the categories associated with the specified application context. + /// + /// The context of the application. + /// An enumeration of category names. + public IEnumerable GetCategories(IApplicationContext applicationContext) + { + return _dict.Values + .SelectMany(a => a) + .Where(a => a.Key == applicationContext) + .SelectMany(c => c.Value) + .Select(c => c.Key); + } + + /// + /// Returns the groups associated with the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve groups. + /// An enumeration of group names. + public IEnumerable GetGroups(IApplicationContext applicationContext, string category) + { + return _dict.Values + .SelectMany(a => a) + .Where(a => a.Key == applicationContext) + .SelectMany(c => c.Value) + .Where(c => c.Key == category) + .SelectMany(s => s.Value) + .SelectMany(g => g.Value) + .Select(g => g.Key); + } + + /// + /// Returns an enumeration of setting page contexts for the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// An enumeration of setting page contexts. + public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category) + { + return _dict.Values + .SelectMany(a => a) + .Where(a => a.Key == applicationContext) + .SelectMany(c => c.Value) + .Where(c => c.Key == category) + .SelectMany(s => s.Value) + .SelectMany(g => g.Value) + .SelectMany(g => g.Value) + .Select(x => x.SettingPageContext); + } + + /// + /// Returns an enumeration of setting page contexts for the specified application context, category, and group. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// The group for which to retrieve setting pages. + /// An enumeration of setting page contexts. + public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group) + { + return _dict.Values + .SelectMany(a => a) + .Where(a => a.Key == applicationContext) + .SelectMany(c => c.Value) + .Where(c => c.Key == category) + .SelectMany(s => s.Value) + .SelectMany(g => g.Value) + .Where(g => g.Key == group) + .SelectMany(g => g.Value) + .Select(x => x.SettingPageContext); + } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs similarity index 88% rename from src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs rename to src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs index 5f3eb3b..eaa16c5 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs @@ -3,9 +3,9 @@ namespace WebExpress.WebCore.WebSettingPage.Model { /// - /// Represents a context for setting page dictionary items, inheriting from Dictionary. + /// Represents a category for setting page dictionary items, inheriting from dictionary. /// - public class SettingPageDictionaryItemContext : Dictionary + public class SettingPageDictionaryItemCategory : Dictionary { /// /// Adds a setting page item to the dictionary. @@ -22,7 +22,7 @@ public bool AddSettingPageItem(string context, SettingSection section, string gr // register context if (!ContainsKey(context)) { - Add(context, new SettingPageDictionaryItemSection()); + Add(context, []); } return this[context].AddSettingPageItem(section, group, page); diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 27e1ac2..85838cb 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -28,9 +28,9 @@ public class SettingPageContext : PageContext, ISettingPageContext public string Icon { get; internal set; } /// - /// Returns the setting context. + /// Returns the setting category. /// - public string Context { get; internal set; } + public string Category { get; internal set; } /// /// Returns the section of the setting page. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index b34ce2d..e38bb16 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -25,7 +25,7 @@ public sealed class SettingPageManager : ISettingPageManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly SettingPageDictionary _dictionary = []; + private readonly SettingPageDictionary _dictionary = new(); private static readonly Dictionary _delegateCache = []; /// @@ -41,7 +41,7 @@ public sealed class SettingPageManager : ISettingPageManager /// /// Returns the collection of setting pages. /// - public IEnumerable SettingPages => _dictionary.SettingPages; + public IEnumerable SettingPages => _dictionary.All; /// /// Initializes a new instance of the class. @@ -154,34 +154,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe /// The created or cached page. private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContext) { - var settingPageItem = _dictionary.Values - .SelectMany(a => a.Values) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) - .FirstOrDefault(x => x.SettingPageContext.Equals(settingPageContext)); - - if (settingPageItem != null && settingPageItem.Instance == null) - { - var instance = ComponentActivator.CreateInstance - ( - settingPageItem.SettingPageClass, - settingPageContext, - _httpServerContext, - _componentHub, - settingPageContext.ApplicationContext - ); - - if (settingPageItem.Cache) - { - settingPageItem.Instance = instance; - } - - return instance; - } - - return settingPageItem?.Instance; + return _dictionary.CreateSettingPageInstance(settingPageContext, _componentHub, _httpServerContext); } /// @@ -190,7 +163,7 @@ private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageConte /// The context of the plugin whose setting pages are to be associated. private void Register(IPluginContext pluginContext) { - if (_dictionary.ContainsKey(pluginContext)) + if (_dictionary.Contains(pluginContext)) { return; } @@ -204,13 +177,13 @@ private void Register(IPluginContext pluginContext) /// The context of the application whose pages are to be associated. private void Register(IApplicationContext applicationContext) { - foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + if (_dictionary.Contains(applicationContext)) { - if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) - { - continue; - } + return; + } + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { Register(pluginContext, [applicationContext]); } } @@ -234,7 +207,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); - var context = default(string); + var category = default(string); var group = default(string); var section = SettingSection.Primary; var hide = false; @@ -257,9 +230,9 @@ private void Register(IPluginContext pluginContext, IEnumerableThe context of the application that contains the fragments to remove. internal void Remove(IApplicationContext applicationContext) { - if (applicationContext == null) + foreach (var settingPageContext in _dictionary.Remove(applicationContext)) { - return; - } - - foreach (var pluginDict in _dictionary.Values) - { - foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) - { - foreach (var settingPageItem in appDict.Values - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i)) - { - OnRemoveSettingPage(settingPageItem.SettingPageContext); - settingPageItem.Dispose(); - } - } - - pluginDict.Remove(applicationContext); + OnRemoveSettingPage(settingPageContext); } } @@ -397,14 +353,7 @@ internal void Remove(IApplicationContext applicationContext) /// An enumeration of setting page contextes. public IEnumerable GetSettingPages(Type settingPageType) { - return _dictionary.Values - .SelectMany(a => a.Values) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) - .Where(x => x.SettingPageClass.Equals(settingPageType)) - .Select(x => x.SettingPageContext); + return _dictionary.GetSettingPages(settingPageType); } /// @@ -415,16 +364,82 @@ public IEnumerable GetSettingPages(Type settingPageType) /// An enumeration of setting page contextes. public IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext) { - return _dictionary.Values - .SelectMany(a => a) - .Where(a => a.Key.Equals(applicationContext)) - .Select(a => a.Value) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) - .Where(x => x.SettingPageClass.Equals(settingPageType)) - .Select(x => x.SettingPageContext); + return _dictionary.GetSettingPages(settingPageType, applicationContext); + } + + /// + /// Returns the categories associated with the specified application context. + /// + /// The context of the application. + /// An enumeration of category names. + public IEnumerable GetCategories(IApplicationContext applicationContext) + { + return _dictionary.GetCategories(applicationContext); + } + + /// + /// Returns the groups associated with the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve groups. + /// An enumeration of group names. + public IEnumerable GetGroups(IApplicationContext applicationContext, string category) + { + return _dictionary.GetGroups(applicationContext, category); + } + + /// + /// Returns an enumeration of setting page contexts for the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// An enumeration of setting page contexts. + public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category) + { + return _dictionary.GetSettingPages(applicationContext, category); + } + + /// + /// Returns the first setting page context for the specified application context and category. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// The first setting page context or null. + public ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, string category) + { + var pages = _dictionary.GetSettingPages(applicationContext, category); + var preferences = pages.Where(x => x.Section == SettingSection.Preferences); + var primary = pages.Where(x => x.Section == SettingSection.Primary); + var secondary = pages.Where(x => x.Section == SettingSection.Secondary); + + if (preferences.Any()) + { + return preferences.OrderBy(x => x.PageTitle).FirstOrDefault(); + } + + if (primary.Any()) + { + return primary.OrderBy(x => x.PageTitle).FirstOrDefault(); + } + + if (secondary.Any()) + { + return secondary.OrderBy(x => x.PageTitle).FirstOrDefault(); + } + + return null; + } + + /// + /// Returns an enumeration of setting page contexts for the specified application context, category, and group. + /// + /// The context of the application. + /// The category for which to retrieve setting pages. + /// The group for which to retrieve setting pages. + /// An enumeration of setting page contexts. + public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group) + { + return _dictionary.GetSettingPages(applicationContext, category, group); } /// From 51fcf37268fd4c1ed85a8a3de8a04873cbe1f623 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Thu, 6 Mar 2025 16:13:21 +0100 Subject: [PATCH 131/162] feat: add new translate function with renderContext parameter --- .../Internationalization/I18N.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/WebExpress.WebCore/Internationalization/I18N.cs b/src/WebExpress.WebCore/Internationalization/I18N.cs index a2c2c2a..ec8a0f7 100644 --- a/src/WebExpress.WebCore/Internationalization/I18N.cs +++ b/src/WebExpress.WebCore/Internationalization/I18N.cs @@ -1,5 +1,6 @@ using System.Globalization; using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebPage; namespace WebExpress.WebCore.Internationalization { @@ -52,6 +53,29 @@ public static string Translate(Request request, string key, params object[] args return WebEx.ComponentHub?.InternationalizationManager?.Translate(request, key, args) ?? key; } + /// + /// Translates a given key to the specified language. + /// + /// The render context with the language to use. + /// The internationalization key. + /// The value of the key in the current language. + public static string Translate(IRenderContext renderContext, string key) + { + return WebEx.ComponentHub?.InternationalizationManager?.Translate(renderContext?.Request, key) ?? key; + } + + /// + /// Translates a given key to the specified language. + /// + /// The render context with the language to use. + /// The internationalization key. + /// The formatting arguments. + /// The value of the key in the current language. + public static string Translate(IRenderContext renderContext, string key, params object[] args) + { + return WebEx.ComponentHub?.InternationalizationManager?.Translate(renderContext?.Request, key, args) ?? key; + } + /// /// Translates a given key to the specified language. /// From 1ec1168e0a0ce200be3a9930f4f2cb84ed2dc6d0 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 9 Mar 2025 02:14:17 +0100 Subject: [PATCH 132/162] feat: enhance setting page manager functionality --- .../Manager/UnitTestSettingPageManager.cs | 191 ++++++++- .../TestSettingCategoryA.cs | 17 + .../TestSettingCategoryB.cs | 17 + .../TestSettingCategoryC.cs | 17 + .../TestSettingGroupA.cs | 18 + .../TestSettingGroupB.cs | 18 + .../TestSettingGroupC.cs | 17 + .../TestSettingPageA.cs | 1 + .../TestSettingPageB.cs | 1 + .../TestSettingPageC.cs | 49 +++ .../WebExpress.WebCore.Test.csproj | 4 + src/WebExpress.WebCore/HttpServer.cs | 1 - .../Internationalization/de | 11 + .../Internationalization/en | 11 + .../WebAttribute/DescriptionAttribute.cs | 2 +- .../WebAttribute/ISettingCategoryAttribute.cs | 9 + .../WebAttribute/ISettingGroupAttribute.cs | 9 + .../WebAttribute/IconAttribute.cs | 2 +- .../WebAttribute/NameAttribute.cs | 2 +- .../WebAttribute/SectionAttribute.cs | 2 +- .../WebAttribute/SettingCategoryAttribute.cs | 8 +- .../WebAttribute/SettingGroupAttribute.cs | 12 +- .../WebAttribute/SettingSectionAttribute.cs | 2 +- .../WebSettingPage/ISettingCategory.cs | 9 + .../WebSettingPage/ISettingCategoryContext.cs | 48 +++ .../WebSettingPage/ISettingGroup.cs | 9 + .../WebSettingPage/ISettingGroupContext.cs | 53 +++ .../WebSettingPage/ISettingPageContext.cs | 18 +- .../WebSettingPage/ISettingPageManager.cs | 25 +- .../Model/SettingCategoryDictionary.cs | 116 +++++ .../Model/SettingCategoryItem.cs | 55 +++ .../Model/SettingGroupDictionary.cs | 132 ++++++ .../WebSettingPage/Model/SettingGroupItem.cs | 60 +++ .../Model/SettingPageDictionary.cs | 108 ++--- .../SettingPageDictionaryItemCategory.cs | 66 --- .../Model/SettingPageDictionaryItemGroup.cs | 94 ----- .../Model/SettingPageDictionaryItemSection.cs | 105 ----- .../WebSettingPage/Model/SettingPageItem.cs | 14 +- .../WebSettingPage/SettingCategoryContext.cs | 57 +++ .../WebSettingPage/SettingGroupContext.cs | 62 +++ .../WebSettingPage/SettingPageContext.cs | 22 +- .../WebSettingPage/SettingPageManager.cs | 397 +++++++++++++++--- 42 files changed, 1406 insertions(+), 465 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestSettingCategoryA.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingCategoryB.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingCategoryC.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingGroupA.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingGroupB.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingGroupC.cs create mode 100644 src/WebExpress.WebCore.Test/TestSettingPageC.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/ISettingCategoryAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/ISettingGroupAttribute.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingCategory.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingGroup.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryDictionary.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryItem.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupDictionary.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupItem.cs delete mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs delete mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs delete mode 100644 src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs create mode 100644 src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index d899fec..4164165 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -1,6 +1,7 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test.Manager { @@ -14,13 +15,39 @@ public class UnitTestSettingPageManager /// Test the register function of the setting page manager. /// [Fact] - public void Register() + public void RegisterSettingPages() { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(6, componentHub.SettingPageManager.SettingPages.Count()); + Assert.Equal(9, componentHub.SettingPageManager.SettingPages.Count()); + } + + /// + /// Test the register function of the setting page manager. + /// + [Fact] + public void RegisterSettingCategories() + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(9, componentHub.SettingPageManager.SettingCategories.Count()); + } + + /// + /// Test the register function of the setting page manager. + /// + [Fact] + public void RegisterSettingGroups() + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(9, componentHub.SettingPageManager.SettingGroups.Count()); } /// @@ -131,5 +158,165 @@ public void IsIContext() Assert.True(typeof(IContext).IsAssignableFrom(settingPages.GetType()), $"Page context {settingPages.GetType().Name} does not implement IContext."); } } + + /// + /// Test the name property of the setting categories. + /// + [Theory] + [InlineData(typeof(TestApplicationA), new[] { "SettingCategory A", "SettingCategory B", "SettingCategory C" })] + [InlineData(typeof(TestApplicationB), new[] { "SettingCategory A", "SettingCategory B", "SettingCategory C" })] + [InlineData(typeof(TestApplicationC), new[] { "SettingCategory A", "SettingCategory B", "SettingCategory C" })] + public void CategoryName(Type applicationType, params string[] names) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategories = componentHub.SettingPageManager.GetSettingCategories(application); + + // test execution + Assert.Equal([.. names], [.. settingCategories.Select(x => x.Name)]); + } + + /// + /// Test the description property of the setting categories. + /// + [Theory] + [InlineData(typeof(TestApplicationA), new[] { "Description of category a.", "Description of category b.", "Description of category c." })] + [InlineData(typeof(TestApplicationB), new[] { "Description of category a.", "Description of category b.", "Description of category c." })] + [InlineData(typeof(TestApplicationC), new[] { "Description of category a.", "Description of category b.", "Description of category c." })] + public void CategoryDescription(Type applicationType, params string[] descriptions) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategories = componentHub.SettingPageManager.GetSettingCategories(application); + + // test execution + Assert.Equal([.. descriptions], [.. settingCategories.Select(x => x.Description)]); + } + + /// + /// Test the section property of the setting categories. + /// + [Theory] + [InlineData(typeof(TestApplicationA), new[] { SettingSection.Preferences, SettingSection.Primary, SettingSection.Secondary })] + [InlineData(typeof(TestApplicationB), new[] { SettingSection.Preferences, SettingSection.Primary, SettingSection.Secondary })] + [InlineData(typeof(TestApplicationC), new[] { SettingSection.Preferences, SettingSection.Primary, SettingSection.Secondary })] + public void CategorySection(Type applicationType, params SettingSection[] sections) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategories = componentHub.SettingPageManager.GetSettingCategories(application); + + // test execution + Assert.Equal([.. sections], [.. settingCategories.Select(x => x.Section)]); + } + + /// + /// Test the name property of the setting groups. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), new[] { "SettingGroup A", "SettingGroup B" })] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), new[] { "SettingGroup A", "SettingGroup B" })] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), new[] { "SettingGroup A", "SettingGroup B" })] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), new string[0])] + [InlineData(typeof(TestApplicationA), null, new[] { "SettingGroup C" })] + public void GroupName(Type applicationType, Type settingCategoryType, params string[] names) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategory = componentHub.SettingPageManager.GetSettingCategories(application).FirstOrDefault(x => x.CategoryId.ToString() == settingCategoryType?.FullName.ToLower()); + var settinGroups = componentHub.SettingPageManager.GetSettingGroups(application, settingCategory); + + // test execution + Assert.Equal([.. names], [.. settinGroups.Select(x => x.Name)]); + } + + /// + /// Test the description property of the setting groups. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), new[] { "Description of group a.", "Description of group b." })] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), new[] { "Description of group a.", "Description of group b." })] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), new[] { "Description of group a.", "Description of group b." })] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), new string[0])] + [InlineData(typeof(TestApplicationA), null, new[] { "Description of group c." })] + public void GroupDescription(Type applicationType, Type settingCategoryType, params string[] descriptions) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategory = componentHub.SettingPageManager.GetSettingCategories(application).FirstOrDefault(x => x.CategoryId.ToString() == settingCategoryType?.FullName.ToLower()); + var settinGroups = componentHub.SettingPageManager.GetSettingGroups(application, settingCategory); + + // test execution + Assert.Equal([.. descriptions], [.. settinGroups.Select(x => x.Description)]); + } + + /// + /// Test the section property of the setting groups. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), new SettingSection[0])] + [InlineData(typeof(TestApplicationA), null, new[] { SettingSection.Secondary })] + public void GroupSection(Type applicationType, Type settingCategoryType, params SettingSection[] sections) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategory = componentHub.SettingPageManager.GetSettingCategories(application).FirstOrDefault(x => x.CategoryId.ToString() == settingCategoryType?.FullName.ToLower()); + var settinGroups = componentHub.SettingPageManager.GetSettingGroups(application, settingCategory); + + // test execution + Assert.Equal([.. sections], [.. settinGroups.Select(x => x.Section)]); + } + + /// + /// Test the category property of the setting groups. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), new SettingSection[0])] + [InlineData(typeof(TestApplicationA), null, new[] { SettingSection.Secondary })] + public void GroupCategory(Type applicationType, Type settingCategoryType, params SettingSection[] sections) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategory = componentHub.SettingPageManager.GetSettingCategories(application).FirstOrDefault(x => x.CategoryId.ToString() == settingCategoryType?.FullName.ToLower()); + var settinGroups = componentHub.SettingPageManager.GetSettingGroups(application, settingCategory); + + // test execution + Assert.Equal(settinGroups.Count(), settinGroups.Where(x => x.SettingCategory == settingCategory).Count()); + } + + /// + /// Test the GetFirstSettingPage function of the setting manager. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), typeof(TestSettingPageA))] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), typeof(TestSettingPageA))] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), typeof(TestSettingPageA))] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), null)] + [InlineData(typeof(TestApplicationA), null, typeof(TestSettingPageC))] + public void GetFirstSettingPage(Type applicationType, Type settingCategoryType, Type firstPageType) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategory = componentHub.SettingPageManager.GetSettingCategories(application).FirstOrDefault(x => x.CategoryId.ToString() == settingCategoryType?.FullName.ToLower()); + var firstPage = firstPageType != null ? componentHub.SettingPageManager.GetSettingPages(firstPageType, application).FirstOrDefault() : null; + var settingPage = componentHub.SettingPageManager.GetFirstSettingPage(application, settingCategory); + + // test execution + Assert.Equal(firstPage, settingPage); + } } } diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs new file mode 100644 index 0000000..3e18486 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs @@ -0,0 +1,17 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting category for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingCategory A")] + [Description("Description of category a.")] + [SettingSection(SettingSection.Preferences)] + public sealed class TestSettingCategoryA : ISettingCategory + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs new file mode 100644 index 0000000..8990581 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs @@ -0,0 +1,17 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting category for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingCategory B")] + [Description("Description of category b.")] + [SettingSection(SettingSection.Primary)] + public sealed class TestSettingCategoryB : ISettingCategory + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs new file mode 100644 index 0000000..bb2f910 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs @@ -0,0 +1,17 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting category for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingCategory C")] + [Description("Description of category c.")] + [SettingSection(SettingSection.Secondary)] + public sealed class TestSettingCategoryC : ISettingCategory + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs new file mode 100644 index 0000000..b92229d --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs @@ -0,0 +1,18 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting group for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingGroup A")] + [Description("Description of group a.")] + [SettingCategory()] + [SettingSection(SettingSection.Preferences)] + public sealed class TestSettingGroupA : ISettingGroup + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs new file mode 100644 index 0000000..2d22821 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs @@ -0,0 +1,18 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting group for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingGroup B")] + [Description("Description of group b.")] + [SettingCategory()] + [SettingSection(SettingSection.Primary)] + public sealed class TestSettingGroupB : ISettingGroup + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs new file mode 100644 index 0000000..e6d53de --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs @@ -0,0 +1,17 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebSettingPage; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy setting group for testing purposes. + /// + [Icon("InfoCircle")] + [Name("SettingGroup C")] + [Description("Description of group c.")] + [SettingSection(SettingSection.Secondary)] + public sealed class TestSettingGroupC : ISettingGroup + { + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/TestSettingPageA.cs index f79a101..b112428 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageA.cs @@ -10,6 +10,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:settingpagea.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] + [SettingGroup()] public sealed class TestSettingPageA : ISettingPage { /// diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/TestSettingPageB.cs index 3375812..71ab34d 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageB.cs @@ -10,6 +10,7 @@ namespace WebExpress.WebCore.Test [Title("webindex:settingpageb.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] + [SettingGroup()] public sealed class TestSettingPageB : ISettingPage { /// diff --git a/src/WebExpress.WebCore.Test/TestSettingPageC.cs b/src/WebExpress.WebCore.Test/TestSettingPageC.cs new file mode 100644 index 0000000..d085c33 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestSettingPageC.cs @@ -0,0 +1,49 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebSettingPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:settingpageb.label")] + [Segment("settingpagea", "webindex:homepage.label")] + [ContextPath(null)] + public sealed class TestSettingPageC : ISettingPage + { + /// + /// Returns or sets the setting page context. + /// + public ISettingPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the setting page. + public TestSettingPageC(ISettingPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the setting page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + } +} diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index d7cd038..6d63af8 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -53,6 +53,10 @@ + + + + diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index fe67b56..04968cd 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -187,7 +187,6 @@ private void AddEndpoint(OptionsWrapper serverOptions, End { HttpServerContext.Log.Error(message: I18N.Translate("webexpress.webcore:httpserver.listen.exeption"), args: endPoint); HttpServerContext.Log.Exception(ex); - } } diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 0f05a0e..a310acf 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -126,6 +126,17 @@ sitemapmanager.merge.error=Die beiden Sitemaps '{0}' und '{1}' konnten nicht gem sessionmanager.initialization=Der Sessionmanager wurde initialisiert. +settingpagemanager.initialization=Der Settingpagemanager wurde initialisiert. +settingpagemanager.register.category=Die Einstellungskategorie '{0}' wurde der Anwendung '{1}' zugewiesen und im Settingpagemanager registriert. +settingpagemanager.register.group=Die Einstellungsgruppe '{0}' wurde der Anwendung '{1}' zugewiesen und im Settingpagemanager registriert. +settingpagemanager.register.page=Die Einstellungsseite '{0}' wurde der Anwendung '{1}' zugewiesen und im Settingpagemanager registriert. +settingpagemanager.register.nocategory=Die Einstellungsseite '{0}' wurde keiner Einstellungskategorie zugewiesen. +settingpagemanager.register.nogroup=Die Einstellungsseite '{0}' wurde keiner Einstellungsgruppe zugewiesen. +settingpagemanager.category.general.name=Allgemeine Einstellungen +settingpagemanager.category.general.description=Standardkategorie für allgemeine Anwendungseinstellungen. +settingpagemanager.group.general.name=Allgemein +settingpagemanager.group.general.description=Allgemeine Anwendungseinstellungen. + eventmanager.titel=Eventmanager: eventmanager.initialization=Der Eventmanager wurde initialisiert. eventmanager.register=Der Eventhandler '{0}' wurde der Anwendung '{1}' zugewiesen und im Eventmanager registriert. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index de3f033..3b28a81 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -126,6 +126,17 @@ sitemapmanager.merge.error=The two sitemaps '{0}' and '{1}' could not be merged. sessionmanager.initialization=The session manager has been initialized. +settingpagemanager.initialization=The setting page manager has been initialized. +settingpagemanager.register.category=The setting category '{0}' has been registered in the application '{1}'. +settingpagemanager.register.group=The setting group '{0}' has been registered in the application '{1}'. +settingpagemanager.register.page=The setting page '{0}' has been registered in the application '{1}'. +settingpagemanager.register.nocategory=The setting page '{0}' has not been assigned to a setting category. +settingpagemanager.register.nogroup=The setting page '{0}' has not been assigned to a setting group. +settingpagemanager.category.general.name=General settings +settingpagemanager.category.general.description=Default category for general application settings. +settingpagemanager.group.general.name=General +settingpagemanager.group.general.description=General application settings. + eventmanager.titel=Event manager: eventmanager.initialization=The event manager has been initialized. eventmanager.register=The event handler '{0}' has been registered in the application '{1}'. diff --git a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs index b33b649..ebc7138 100644 --- a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Implements , , and . /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class DescriptionAttribute : System.Attribute, IPluginAttribute, IApplicationAttribute + public class DescriptionAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/ISettingCategoryAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ISettingCategoryAttribute.cs new file mode 100644 index 0000000..618eb8c --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ISettingCategoryAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a setting category assignment attribute. + /// + public interface ISettingCategoryAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/ISettingGroupAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ISettingGroupAttribute.cs new file mode 100644 index 0000000..695cd6f --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ISettingGroupAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a setting group assignment attribute. + /// + public interface ISettingGroupAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 7f8643f..1b228bd 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to specify an icon for a plugin, application, or status page. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class IconAttribute : Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute + public class IconAttribute : Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute, ISettingPageAttribute, ISettingCategoryAttribute, ISettingGroupAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs index e67b7bd..0a978b6 100644 --- a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to assign a name to a class. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class NameAttribute : Attribute, IPluginAttribute, IApplicationAttribute + public class NameAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs index d2ff7b3..5445fd6 100644 --- a/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SectionAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to identify a section. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - public class SectionAttribute : Attribute, IFragmentAttribute where T : class, ISection + public class SectionAttribute : Attribute, IFragmentAttribute, ISettingCategoryAttribute, ISettingGroupAttribute where T : class, ISection { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs index 19665c1..ee798b9 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingCategoryAttribute.cs @@ -1,4 +1,5 @@ using System; +using WebExpress.WebCore.WebSettingPage; namespace WebExpress.WebCore.WebAttribute { @@ -6,15 +7,14 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to specify the category in which the settings page is associated. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SettingCategoryAttribute : Attribute, IEndpointAttribute + public class SettingCategoryAttribute : Attribute, ISettingGroupAttribute + where TCategory : class, ISettingCategory { /// /// Initializes a new instance of the class. /// - /// The category in which the settings page is associated. - public SettingCategoryAttribute(string category) + public SettingCategoryAttribute() { - } } } diff --git a/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs index 8f588a0..5da22f1 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingGroupAttribute.cs @@ -1,20 +1,20 @@ using System; +using WebExpress.WebCore.WebSettingPage; namespace WebExpress.WebCore.WebAttribute { /// - /// Attribute to define a setting group for identity. + /// Attribute to define a setting group in which the settings page is associated. /// - [AttributeUsage(AttributeTargets.Class)] - public class SettingGroupAttribute : System.Attribute, IEndpointAttribute + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class SettingGroupAttribute : Attribute, IEndpointAttribute + where TGroup : class, ISettingGroup { /// /// Initializes a new instance of the class. /// - /// The group name. - public SettingGroupAttribute(string name) + public SettingGroupAttribute() { - } } } diff --git a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs index 987cd46..74785dc 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to specify the section where the settings page is listed. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class SettingSectionAttribute : Attribute, IEndpointAttribute + public class SettingSectionAttribute : Attribute, IEndpointAttribute, ISettingCategoryAttribute, ISettingGroupAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingCategory.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingCategory.cs new file mode 100644 index 0000000..851ca0c --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingCategory.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Represents a category of settings in the web application. + /// + public interface ISettingCategory + { + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs new file mode 100644 index 0000000..3e56bc8 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs @@ -0,0 +1,48 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Provides context for setting categories within the web setting page. + /// + public interface ISettingCategoryContext : IContext + { + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + + /// + /// Returns the category id. + /// + IComponentId CategoryId { get; } + + /// + /// Returns the icon. + /// + string Icon { get; } + + /// + /// Returns the name. + /// + string Name { get; } + + /// + /// Returns the description. + /// + string Description { get; } + + /// + /// Returns the section. + /// + SettingSection Section { get; } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingGroup.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingGroup.cs new file mode 100644 index 0000000..cc2ebc9 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingGroup.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Represents a group of settings in the web application. + /// + public interface ISettingGroup + { + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs new file mode 100644 index 0000000..f7ef773 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs @@ -0,0 +1,53 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Provides the context for a setting group. + /// + public interface ISettingGroupContext : IContext + { + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + + /// + /// Returns the setting category context to which the setting group belongs. + /// + ISettingCategoryContext SettingCategory { get; } + + /// + /// Returns the group id. + /// + IComponentId GroupId { get; } + + /// + /// Returns the icon. + /// + string Icon { get; } + + /// + /// Returns the name. + /// + string Name { get; } + + /// + /// Returns the description. + /// + string Description { get; } + + /// + /// Returns the section. + /// + SettingSection Section { get; } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index 72438da..633f65d 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -10,14 +10,19 @@ namespace WebExpress.WebCore.WebSettingPage public interface ISettingPageContext : IPageContext { /// - /// Returns the group to which the setting page belongs. + /// Returns the icon. /// - string Group { get; } + string Icon { get; } + + /// + /// Returns the setting category context to which the setting page belongs. + /// + ISettingCategoryContext SettingCategory { get; } /// - /// Returns the setting category. + /// Returns the group context to which the setting page belongs. /// - string Category { get; } + ISettingGroupContext SettingGroup { get; } /// /// Returns the section to which the setting page belongs. @@ -28,10 +33,5 @@ public interface ISettingPageContext : IPageContext /// Returns a value indicating whether the page should be displayed or hidden. /// bool Hide { get; } - - /// - /// Returns or sets the icon. - /// - string Icon { get; } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs index f783095..3b8a888 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageManager.cs @@ -20,6 +20,16 @@ public interface ISettingPageManager : IComponentManager /// event EventHandler RemoveSettingPage; + /// + /// Returns the collection of setting categories. + /// + IEnumerable SettingCategories { get; } + + /// + /// Returns the collection of setting groups. + /// + IEnumerable SettingGroups { get; } + /// /// Returns the collection of setting pages. /// @@ -46,16 +56,15 @@ public interface ISettingPageManager : IComponentManager /// The context of the application. /// The category for which to retrieve setting pages. /// An enumeration of setting page contexts. - IEnumerable GetSettingPages(IApplicationContext applicationContext, string category); + IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingCategoryContext category); /// /// Returns an enumeration of setting page contexts for the specified application context, category, and group. /// /// The context of the application. - /// The category for which to retrieve setting pages. /// The group for which to retrieve setting pages. /// An enumeration of setting page contexts. - IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group); + IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingGroupContext group); /// /// Returns the first setting page context for the specified application context and category. @@ -63,21 +72,21 @@ public interface ISettingPageManager : IComponentManager /// The context of the application. /// The category for which to retrieve setting pages. /// The first setting page context or null. - ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, string category); + ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, ISettingCategoryContext category); /// /// Returns the categories associated with the specified application context. /// /// The context of the application. - /// An enumeration of category names. - IEnumerable GetCategories(IApplicationContext applicationContext); + /// An enumeration of category contexts associated with the specified application context. + IEnumerable GetSettingCategories(IApplicationContext applicationContext); /// /// Returns the groups associated with the specified application context and category. /// /// The context of the application. /// The category for which to retrieve groups. - /// An enumeration of group names. - IEnumerable GetGroups(IApplicationContext applicationContext, string category); + /// An enumeration of setting group contexts associated with the provided application context and category. + IEnumerable GetSettingGroups(IApplicationContext applicationContext, ISettingCategoryContext category); } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryDictionary.cs new file mode 100644 index 0000000..9b27e21 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryDictionary.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents a dictionary for managing setting categories, organized by plugin and application contexts. + /// + internal class SettingCategoryDictionary + { + private readonly Dictionary>> _dict = []; + + /// + /// Returns the collection of setting categories. + /// + public IEnumerable All => _dict.Values + .SelectMany(a => a.Values) + .SelectMany(x => x) + .Select(x => x.SettingCategoryContext); + + /// + /// Adds a settings category. + /// + /// The settings category to insert. + /// True if the settings category was added successfully; otherwise, false. + public bool AddSettingCategoryItem(SettingCategoryItem item) + { + if (!_dict.TryGetValue(item.PluginContext, out var appDict)) + { + appDict = []; + _dict.Add(item.PluginContext, appDict); + } + + if (!appDict.TryGetValue(item.ApplicationContext, out var list)) + { + list = []; + appDict.Add(item.ApplicationContext, list); + } + + list.Add(item); + + return true; + } + + /// + /// Removes all elements associated with the specified plugin context. + /// + /// The context of the plugin that contains the elements to remove. + public void Remove(IPluginContext pluginContext) + { + _dict.Remove(pluginContext); + } + + /// + /// Removes all setting categories associated with the specified application context. + /// + /// The context of the application that contains the fragments to remove. + /// An enumerable collection of setting categories contexts that were removed. + public IEnumerable Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + yield break; + } + + foreach (var pluginDict in _dict.Values) + { + var removed = pluginDict + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .ToList(); + + pluginDict.Remove(applicationContext); + + foreach (var category in removed) + { + yield return category.SettingCategoryContext; + } + } + } + + /// + /// Returns the setting category context for the specified application context. + /// + /// The application context. + /// An enumeration of category contexts associated with the specified application context. + public IEnumerable GetSettingCategories(IApplicationContext applicationContext) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Select(x => x.SettingCategoryContext); + } + + /// + /// Returns the setting category context for the specified application context and category type. + /// + /// The application context. + /// The type of the category. + /// >The setting category context if found; otherwise, null. + public ISettingCategoryContext GetSettingCategory(IApplicationContext applicationContext, Type categoryType) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.SettingCategoryClass == categoryType) + .Select(x => x.SettingCategoryContext) + .FirstOrDefault(); + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryItem.cs new file mode 100644 index 0000000..26b38e7 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingCategoryItem.cs @@ -0,0 +1,55 @@ +using System; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents an item on the setting category. + /// + public class SettingCategoryItem : IDisposable + { + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the setting category context. + /// + public ISettingCategoryContext SettingCategoryContext { get; internal set; } + + /// + /// Returns the class type of the setting category. + /// + public Type SettingCategoryClass { get; internal set; } + + /// + /// Returns the human-readable name or a internationalization key of the category. + /// + public string Name { get; internal set; } + + /// + /// Returns the human-readable description or a internationalization key of the category. + /// + public string Description { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupDictionary.cs new file mode 100644 index 0000000..d46cb95 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupDictionary.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents a dictionary for managing setting groups, organized by plugin and application contexts. + /// + internal class SettingGroupDictionary + { + private readonly Dictionary>> _dict = new(); + + /// + /// Returns the collection of setting groups. + /// + public IEnumerable All => _dict.Values + .SelectMany(a => a.Values) + .SelectMany(x => x) + .Select(x => x.SettingGroupContext); + + /// + /// Adds a settings group. + /// + /// The settings group to insert. + /// True if the settings group was added successfully; otherwise, false. + public bool AddSettingGroupItem(SettingGroupItem item) + { + if (!_dict.TryGetValue(item.PluginContext, out var appDict)) + { + appDict = []; + _dict.Add(item.PluginContext, appDict); + } + + if (!appDict.TryGetValue(item.ApplicationContext, out var list)) + { + list = []; + appDict.Add(item.ApplicationContext, list); + } + + list.Add(item); + + return true; + } + + /// + /// Removes all elements associated with the specified plugin context. + /// + /// The context of the plugin that contains the elements to remove. + public void Remove(IPluginContext pluginContext) + { + _dict.Remove(pluginContext); + } + + /// + /// Removes all setting groups associated with the specified application context. + /// + /// The context of the application that contains the fragments to remove. + /// An enumerable collection of setting groups contexts that were removed. + public IEnumerable Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + yield break; + } + + foreach (var pluginDict in _dict.Values) + { + var removed = pluginDict + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .ToList(); + + pluginDict.Remove(applicationContext); + + foreach (var category in removed) + { + yield return category.SettingGroupContext; + } + } + } + + /// + /// Returns the setting group context for the specified application context and group type. + /// + /// The application context. + /// An enumeration of category contexts associated with the specified application context. + public IEnumerable GetSettingGroups(IApplicationContext applicationContext) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Select(x => x.SettingGroupContext); + } + + /// + /// Returns the setting group context for the specified application context and group type. + /// + /// The application context. + /// The category context. + /// An enumeration of category contexts associated with the specified application context. + public IEnumerable GetSettingGroups(IApplicationContext applicationContext, ISettingCategoryContext categoryContext) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.SettingGroupContext.SettingCategory == categoryContext) + .Select(x => x.SettingGroupContext); + } + + /// + /// Returns the setting group context for the specified application context and group type. + /// + /// The application context. + /// The type of the group. + /// The setting group context if found; otherwise, null. + public ISettingGroupContext GetSettingGroup(IApplicationContext applicationContext, Type groupType) + { + return _dict.Values + .SelectMany(x => x) + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .Where(x => x.SettingGroupClass == groupType) + .Select(x => x.SettingGroupContext) + .FirstOrDefault(); + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupItem.cs new file mode 100644 index 0000000..ab87a25 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingGroupItem.cs @@ -0,0 +1,60 @@ +using System; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebSettingPage.Model +{ + /// + /// Represents an item on the setting group. + /// + public class SettingGroupItem : IDisposable + { + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the setting group context. + /// + public ISettingGroupContext SettingGroupContext { get; internal set; } + + /// + /// Returns the class type of the setting group. + /// + public Type SettingGroupClass { get; internal set; } + + /// + /// Returns the human-readable name or a internationalization key of the group. + /// + public string Name { get; internal set; } + + /// + /// Returns the human-readable description or a internationalization key of the group. + /// + public string Description { get; internal set; } + + /// + /// Returns the setting category. + /// + public Type Category { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + GC.SuppressFinalize(this); + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs index dae2692..cbbbcf2 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionary.cs @@ -13,17 +13,14 @@ namespace WebExpress.WebCore.WebSettingPage.Model /// internal class SettingPageDictionary { - private readonly Dictionary> _dict = []; + private readonly Dictionary>> _dict = []; /// /// Returns the collection of setting pages. /// public IEnumerable All => _dict.Values .SelectMany(a => a.Values) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) + .SelectMany(x => x) .Select(x => x.SettingPageContext); /// @@ -39,13 +36,15 @@ public bool AddSettingPageItem(SettingPageItem item) _dict.Add(item.PluginContext, appDict); } - if (!appDict.TryGetValue(item.ApplicationContext, out var contextDict)) + if (!appDict.TryGetValue(item.ApplicationContext, out var list)) { - contextDict = []; - appDict.Add(item.ApplicationContext, contextDict); + list = []; + appDict.Add(item.ApplicationContext, list); } - return contextDict.AddSettingPageItem(item.Context, item.Section, item.Group, item); + list.Add(item); + + return true; } /// @@ -71,18 +70,17 @@ public IEnumerable Remove(IApplicationContext applicationCo foreach (var pluginDict in _dict.Values) { - foreach (var appDict in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) - { - foreach (var settingPageItem in appDict.Values - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i)) - { - yield return settingPageItem.SettingPageContext; - } - } + var removed = pluginDict + .Where(x => x.Key == applicationContext) + .SelectMany(x => x.Value) + .ToList(); pluginDict.Remove(applicationContext); + + foreach (var page in removed) + { + yield return page.SettingPageContext; + } } } @@ -95,10 +93,7 @@ public IEnumerable GetSettingPages(Type settingPageType) { return _dict.Values .SelectMany(a => a.Values) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) + .SelectMany(x => x) .Where(x => x.SettingPageClass.Equals(settingPageType)) .Select(x => x.SettingPageContext); } @@ -114,11 +109,7 @@ public IEnumerable GetSettingPages(Type settingPageType, IA return _dict.Values .SelectMany(a => a) .Where(a => a.Key.Equals(applicationContext)) - .Select(a => a.Value) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) + .SelectMany(x => x.Value) .Where(x => x.SettingPageClass.Equals(settingPageType)) .Select(x => x.SettingPageContext); } @@ -154,10 +145,7 @@ public IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContex { var settingPageItem = _dict.Values .SelectMany(a => a.Values) - .SelectMany(c => c.Values) - .SelectMany(s => s.Values) - .SelectMany(g => g.Values) - .SelectMany(i => i) + .SelectMany(x => x) .FirstOrDefault(x => x.SettingPageContext.Equals(settingPageContext)); if (settingPageItem != null && settingPageItem.Instance == null) @@ -182,54 +170,19 @@ public IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContex return settingPageItem?.Instance; } - /// - /// Returns the categories associated with the specified application context. - /// - /// The context of the application. - /// An enumeration of category names. - public IEnumerable GetCategories(IApplicationContext applicationContext) - { - return _dict.Values - .SelectMany(a => a) - .Where(a => a.Key == applicationContext) - .SelectMany(c => c.Value) - .Select(c => c.Key); - } - - /// - /// Returns the groups associated with the specified application context and category. - /// - /// The context of the application. - /// The category for which to retrieve groups. - /// An enumeration of group names. - public IEnumerable GetGroups(IApplicationContext applicationContext, string category) - { - return _dict.Values - .SelectMany(a => a) - .Where(a => a.Key == applicationContext) - .SelectMany(c => c.Value) - .Where(c => c.Key == category) - .SelectMany(s => s.Value) - .SelectMany(g => g.Value) - .Select(g => g.Key); - } - /// /// Returns an enumeration of setting page contexts for the specified application context and category. /// /// The context of the application. - /// The category for which to retrieve setting pages. + /// The category for which to retrieve setting pages. /// An enumeration of setting page contexts. - public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category) + public IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingCategoryContext categoryContext) { return _dict.Values .SelectMany(a => a) .Where(a => a.Key == applicationContext) - .SelectMany(c => c.Value) - .Where(c => c.Key == category) - .SelectMany(s => s.Value) - .SelectMany(g => g.Value) - .SelectMany(g => g.Value) + .SelectMany(x => x.Value) + .Where(x => x.SettingPageContext?.SettingCategory == categoryContext) .Select(x => x.SettingPageContext); } @@ -237,20 +190,15 @@ public IEnumerable GetSettingPages(IApplicationContext appl /// Returns an enumeration of setting page contexts for the specified application context, category, and group. /// /// The context of the application. - /// The category for which to retrieve setting pages. - /// The group for which to retrieve setting pages. + /// The group for which to retrieve setting pages. /// An enumeration of setting page contexts. - public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group) + public IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingGroupContext groupContext) { return _dict.Values .SelectMany(a => a) .Where(a => a.Key == applicationContext) - .SelectMany(c => c.Value) - .Where(c => c.Key == category) - .SelectMany(s => s.Value) - .SelectMany(g => g.Value) - .Where(g => g.Key == group) - .SelectMany(g => g.Value) + .SelectMany(x => x.Value) + .Where(x => x.SettingPageContext?.SettingGroup == groupContext) .Select(x => x.SettingPageContext); } } diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs deleted file mode 100644 index eaa16c5..0000000 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemCategory.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebSettingPage.Model -{ - /// - /// Represents a category for setting page dictionary items, inheriting from dictionary. - /// - public class SettingPageDictionaryItemCategory : Dictionary - { - /// - /// Adds a setting page item to the dictionary. - /// - /// The setting context. - /// The section to which the item belongs. - /// The group to which the item belongs. - /// The setting page item to add. - /// True if the setting page item was added successfully; otherwise, false. - public bool AddSettingPageItem(string context, SettingSection section, string group, SettingPageItem page) - { - context ??= "*"; - - // register context - if (!ContainsKey(context)) - { - Add(context, []); - } - - return this[context].AddSettingPageItem(section, group, page); - } - - /// - /// Searches for a setting page by its ID. - /// - /// The ID of the setting page to search for. - /// The setting page found, or null if no page was found. - public SettingPageSearchResult FindPage(string pageId) - { - foreach (var v in this) - { - var path = v.Value.FindPage(pageId); - if (path != null) - { - path.Context = v.Key; - return path; - } - } - - return null; - } - - /// - /// Provides all sections that have the same setting context. - /// - /// The setting context. - /// A listing of all sections of the same context, or null if the context does not exist. - public SettingPageDictionaryItemSection GetSections(string context) - { - if (ContainsKey(context)) - { - return this[context]; - } - - return null; - } - } -} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs deleted file mode 100644 index 124a29f..0000000 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemGroup.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace WebExpress.WebCore.WebSettingPage.Model -{ - /// - /// Represents a group of setting page dictionary items. - /// - public class SettingPageDictionaryItemGroup : Dictionary> - { - /// - /// Adds a setting page item to the specified group. - /// - /// The group to which the item should be added. - /// The setting page item to add. - /// True if the setting page item was added successfully; otherwise, false. - public bool AddSettingPageItem(string group, SettingPageItem page) - { - group ??= string.Empty; - - // Register group if it does not exist. - if (!ContainsKey(group)) - { - Add(group, new List()); - } - - var list = this[group]; - - if (!list.Any(x => x.SettingPageContext.EndpointId == page.SettingPageContext.EndpointId)) - { - list.Add(page); - return true; - } - - return false; - } - - /// - /// Searches for a setting page item based on its ID. - /// - /// The ID of the setting page item to search for. - /// A containing the group and item if found; otherwise, null. - public SettingPageSearchResult FindPage(string pageId) - { - foreach (var v in this) - { - var item = v.Value.Where(x => x.SettingPageContext.EndpointId.ToString().Equals(pageId, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - if (item != null) - { - return new SettingPageSearchResult() { Group = v.Key, Item = item }; - } - } - - return null; - } - - /// - /// Returns the first setting page item in the dictionary. - /// - /// A containing the group and first item if found; otherwise, null. - public SettingPageSearchResult FindFirstPage() - { - var firstItem = default(SettingPageItem); - - foreach (var group in this.OrderBy(x => x.Key)) - { - firstItem = group.Value.FirstOrDefault(); - - if (firstItem != null) - { - return new SettingPageSearchResult() { Group = group.Key, Item = firstItem }; - } - } - - return null; - } - - /// - /// Returns all setting page items in the specified group. - /// - /// The group whose items should be returned. - /// A list of in the specified group; otherwise, null if the group does not exist. - public List GetPages(string group) - { - if (ContainsKey(group)) - { - return this[group]; - } - - return null; - } - } -} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs deleted file mode 100644 index e5f2c0d..0000000 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageDictionaryItemSection.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Collections.Generic; - -namespace WebExpress.WebCore.WebSettingPage.Model -{ - /// - /// Represents a dictionary that maps setting sections to their corresponding groups of setting page items. - /// - public class SettingPageDictionaryItemSection : Dictionary - { - /// - /// Adds an item to the specified section and group. - /// - /// The section to add the item to. - /// The group within the section to add the item to. - /// The item to insert. - /// True if the settings page was added successfully; otherwise, false. - public bool AddSettingPageItem(SettingSection section, string group, SettingPageItem item) - { - // register Section - if (!ContainsKey(section)) - { - Add(section, []); - } - - return this[section].AddSettingPageItem(group, item); - } - - /// - /// Searches for a setting page item based on its ID. - /// - /// The ID of the setting page item to search for. - /// The setting page item found, or null if no item with the specified ID exists. - public SettingPageSearchResult FindPage(string pageId) - { - foreach (var v in this) - { - var path = v.Value.FindPage(pageId); - if (path != null) - { - path.Section = v.Key; - return path; - } - } - - return null; - } - - /// - /// Returns the first setting page item found. - /// - /// The first setting page item found, or null if no items exist. - public SettingPageSearchResult FindFirstPage() - { - var firstPage = default(SettingPageSearchResult); - - if (ContainsKey(SettingSection.Preferences)) - { - firstPage = this[SettingSection.Preferences].FindFirstPage(); - if (firstPage != null) - { - firstPage.Section = SettingSection.Preferences; - - return firstPage; - } - } - else if (ContainsKey(SettingSection.Primary)) - { - firstPage = this[SettingSection.Primary].FindFirstPage(); - if (firstPage != null) - { - firstPage.Section = SettingSection.Primary; - - return firstPage; - } - } - else if (ContainsKey(SettingSection.Secondary)) - { - firstPage = this[SettingSection.Secondary].FindFirstPage(); - if (firstPage != null) - { - firstPage.Section = SettingSection.Secondary; - - return firstPage; - } - } - - return firstPage; - } - - /// - /// Returns the group of items that are in the specified section. - /// - /// The section to retrieve the group from. - /// The group of items in the specified section, or null if the section does not exist. - public SettingPageDictionaryItemGroup GetGroup(SettingSection section) - { - if (ContainsKey(section)) - { - return this[section]; - } - - return null; - } - } -} diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index cf44b95..d5927bc 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -26,30 +26,25 @@ public class SettingPageItem : IDisposable public ISettingPageContext SettingPageContext { get; internal set; } /// - /// Returns or sets the class type of the setting page. + /// Returns the class type of the setting page. /// public Type SettingPageClass { get; internal set; } /// - /// Returns or sets the instance of the setting page, if the page is cached, otherwise null. + /// Returns the instance of the setting page, if the page is cached, otherwise null. /// public IEndpoint Instance { get; internal set; } /// - /// Returns the setting context. + /// Returns the group. /// - public string Context { get; internal set; } + public Type Group { get; internal set; } /// /// Returns the section. /// public SettingSection Section { get; internal set; } - /// - /// Returns the group. - /// - public string Group { get; internal set; } - /// /// Returns a value indicating whether the component is created once and reused on each execution. /// @@ -60,6 +55,7 @@ public class SettingPageItem : IDisposable /// public void Dispose() { + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs new file mode 100644 index 0000000..cdc042a --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs @@ -0,0 +1,57 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Provides context for setting categories within the web setting page. + /// + public class SettingCategoryContext : ISettingCategoryContext + { + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the category id. + /// + public IComponentId CategoryId { get; internal set; } + + /// + /// Returns the icon. + /// + public string Icon { get; internal set; } + + /// + /// Returns the name. + /// + public string Name { get; internal set; } + + /// + /// Returns the description. + /// + public string Description { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Setting category: {CategoryId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs new file mode 100644 index 0000000..d588799 --- /dev/null +++ b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs @@ -0,0 +1,62 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebSettingPage.Model; + +namespace WebExpress.WebCore.WebSettingPage +{ + /// + /// Provides context for setting group within the web setting page. + /// + public class SettingGroupContext : ISettingGroupContext + { + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the setting category context to which the setting group belongs. + /// + public ISettingCategoryContext SettingCategory { get; internal set; } + + /// + /// Returns the group id. + /// + public IComponentId GroupId { get; internal set; } + + /// + /// Returns the icon. + /// + public string Icon { get; internal set; } + + /// + /// Returns the name. + /// + public string Name { get; internal set; } + + /// + /// Returns the description. + /// + public string Description { get; internal set; } + + /// + /// Returns the section. + /// + public SettingSection Section { get; internal set; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + { + return $"Setting group: {GroupId}"; + } + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 85838cb..93c21f1 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -13,30 +13,30 @@ namespace WebExpress.WebCore.WebSettingPage public class SettingPageContext : PageContext, ISettingPageContext { /// - /// Returns the group to which the setting page belongs. - /// - public string Group { get; internal set; } - - /// - /// Returns a value indicating whether the page should be displayed or hidden. + /// Returns the icon. /// - public bool Hide { get; internal set; } + public string Icon { get; internal set; } /// - /// Returns the icon. + /// Returns the setting category context to which the setting page belongs. /// - public string Icon { get; internal set; } + public ISettingCategoryContext SettingCategory { get; internal set; } /// - /// Returns the setting category. + /// Returns the group context to which the setting page belongs. /// - public string Category { get; internal set; } + public ISettingGroupContext SettingGroup { get; internal set; } /// /// Returns the section of the setting page. /// public SettingSection Section { get; internal set; } + /// + /// Returns a value indicating whether the page should be displayed or hidden. + /// + public bool Hide { get; internal set; } + /// /// Initializes a new instance of the class with the specified parent type and context path. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index e38bb16..c2a2e09 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -25,7 +25,9 @@ public sealed class SettingPageManager : ISettingPageManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly SettingPageDictionary _dictionary = new(); + private readonly SettingCategoryDictionary _categoryDictionary = new(); + private readonly SettingGroupDictionary _groupDictionary = new(); + private readonly SettingPageDictionary _pageDictionary = new(); private static readonly Dictionary _delegateCache = []; /// @@ -38,10 +40,40 @@ public sealed class SettingPageManager : ISettingPageManager /// public event EventHandler RemoveSettingPage; + /// + /// An event that fires when an setting category is added. + /// + public event EventHandler AddSettingCategory; + + /// + /// An event that fires when an setting category is removed. + /// + public event EventHandler RemoveSettingCategory; + + /// + /// An event that fires when an setting group is added. + /// + public event EventHandler AddSettingGroup; + + /// + /// An event that fires when an setting group is removed. + /// + public event EventHandler RemoveSettingGroup; + + /// + /// Returns the collection of setting categories. + /// + public IEnumerable SettingCategories => _categoryDictionary.All; + + /// + /// Returns the collection of setting groups. + /// + public IEnumerable SettingGroups => _groupDictionary.All; + /// /// Returns the collection of setting pages. /// - public IEnumerable SettingPages => _dictionary.All; + public IEnumerable SettingPages => _pageDictionary.All; /// /// Initializes a new instance of the class. @@ -144,7 +176,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe _componentHub.EndpointManager.Register(endpointtRegistration); - _httpServerContext.Log.Debug(I18N.Translate("webexpress.webapp:pagesettingmanager.initialization")); + _httpServerContext.Log.Debug(I18N.Translate("webexpress.webapp:settingpagemanager.initialization")); } /// @@ -154,7 +186,7 @@ private SettingPageManager(IComponentHub componentHub, IHttpServerContext httpSe /// The created or cached page. private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageContext) { - return _dictionary.CreateSettingPageInstance(settingPageContext, _componentHub, _httpServerContext); + return _pageDictionary.CreateSettingPageInstance(settingPageContext, _componentHub, _httpServerContext); } /// @@ -163,12 +195,14 @@ private IEndpoint CreateSettingPageInstance(ISettingPageContext settingPageConte /// The context of the plugin whose setting pages are to be associated. private void Register(IPluginContext pluginContext) { - if (_dictionary.Contains(pluginContext)) + if (_pageDictionary.Contains(pluginContext)) { return; } - Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + RegisterCategory(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + RegisterGroup(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + RegisterPage(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); } /// @@ -177,14 +211,207 @@ private void Register(IPluginContext pluginContext) /// The context of the application whose pages are to be associated. private void Register(IApplicationContext applicationContext) { - if (_dictionary.Contains(applicationContext)) + if (_pageDictionary.Contains(applicationContext)) { return; } foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) { - Register(pluginContext, [applicationContext]); + RegisterCategory(pluginContext, [applicationContext]); + RegisterGroup(pluginContext, [applicationContext]); + RegisterPage(pluginContext, [applicationContext]); + } + } + + /// + /// Registers categories for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void RegisterCategory(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext.Assembly; + + foreach (var settingCategoryType in assembly.GetTypes() + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(ISettingCategory).Name) != null)) + { + var id = settingCategoryType.FullName?.ToLower(); + var icon = default(string); + var name = default(string); + var description = default(string); + var section = SettingSection.Primary; + + // determining attributes + foreach (var customAttribute in settingCategoryType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(ISettingCategoryAttribute)))) + { + if (customAttribute.AttributeType == typeof(IconAttribute)) + { + icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(NameAttribute)) + { + name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(DescriptionAttribute)) + { + description = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(SettingSectionAttribute)) + { + section = Enum.Parse(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); + } + } + + // assign the category to existing applications + foreach (var applicationContext in applicationContexts) + { + var settingCategoryContext = new SettingCategoryContext() + { + CategoryId = new ComponentId(id), + ApplicationContext = applicationContext, + PluginContext = pluginContext, + Icon = icon, + Name = name, + Description = description, + Section = section + }; + + // create meta information of the setting category + var settingCategoryItem = new SettingCategoryItem() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + SettingCategoryContext = settingCategoryContext, + SettingCategoryClass = settingCategoryType, + Name = name, + Description = description, + Section = section + }; + + // insert the settings category into the dictionary + if (_categoryDictionary.AddSettingCategoryItem(settingCategoryItem)) + { + OnAddSettingCategory(settingCategoryContext); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress.webcore:settingpagemanager.register.category", + id, + applicationContext.ApplicationId + ) + ); + } + } + } + } + + /// + /// Registers groups for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void RegisterGroup(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext.Assembly; + + foreach (var settingGroupType in assembly.GetTypes() + .Where(x => x.IsClass == true && x.IsSealed && x.IsPublic) + .Where(x => x.GetInterface(typeof(ISettingGroup).Name) != null)) + { + var id = settingGroupType.FullName?.ToLower(); + var icon = default(string); + var name = default(string); + var description = default(string); + var category = default(Type); + var section = SettingSection.Primary; + + // determining attributes + foreach (var customAttribute in settingGroupType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(ISettingGroupAttribute)))) + { + if (customAttribute.AttributeType == typeof(IconAttribute)) + { + icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(NameAttribute)) + { + name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(DescriptionAttribute)) + { + description = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType.IsGenericType && customAttribute.AttributeType.GetGenericTypeDefinition() == typeof(SettingCategoryAttribute<>)) + { + category = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + } + else if (customAttribute.AttributeType == typeof(SettingSectionAttribute)) + { + section = Enum.Parse(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); + } + } + + if (category == default) + { + _httpServerContext?.Log.Warning + ( + I18N.Translate + ( + "webexpress.webcore:settingpagemanager.register.nocategory", + id + ) + ); + } + + // assign the group to existing applications + foreach (var applicationContext in applicationContexts) + { + var settingGroupContext = new SettingGroupContext() + { + GroupId = new ComponentId(id), + ApplicationContext = applicationContext, + PluginContext = pluginContext, + Icon = icon, + Name = name, + Description = description, + SettingCategory = _categoryDictionary.GetSettingCategory(applicationContext, category), + Section = section + }; + + // create meta information of the setting group + var settingGroupItem = new SettingGroupItem() + { + PluginContext = pluginContext, + ApplicationContext = applicationContext, + SettingGroupContext = settingGroupContext, + SettingGroupClass = settingGroupType, + Name = name, + Description = description, + Category = category?.GetType(), + Section = section + }; + + // insert the settings category into the dictionary + if (_groupDictionary.AddSettingGroupItem(settingGroupItem)) + { + OnAddSettingGroup(settingGroupContext); + + _httpServerContext?.Log.Debug + ( + I18N.Translate + ( + "webexpress.webcore:settingpagemanager.register.group", + id, + applicationContext.ApplicationId + ) + ); + } + } } } @@ -193,7 +420,7 @@ private void Register(IApplicationContext applicationContext) /// /// The plugin context. /// The application context (optional). - private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + private void RegisterPage(IPluginContext pluginContext, IEnumerable applicationContexts) { var assembly = pluginContext.Assembly; @@ -207,8 +434,7 @@ private void Register(IPluginContext pluginContext, IEnumerable(); - var category = default(string); - var group = default(string); + var group = default(Type); var section = SettingSection.Primary; var hide = false; var icon = default(string); @@ -222,7 +448,7 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) + else if (customAttribute.AttributeType == typeof(ParentAttribute<>)) { parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } @@ -230,13 +456,9 @@ private void Register(IPluginContext pluginContext, IEnumerable)) { - group = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + group = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } else if (customAttribute.AttributeType == typeof(SettingSectionAttribute)) { @@ -256,6 +478,18 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(ISettingPageAttribute)))) { @@ -274,7 +508,7 @@ private void Register(IPluginContext pluginContext, IEnumerableThe context of the plugin that contains the elemets to remove. public void Remove(IPluginContext pluginContext) { - _dictionary.Remove(pluginContext); + _pageDictionary.Remove(pluginContext); } /// @@ -340,7 +572,7 @@ public void Remove(IPluginContext pluginContext) /// The context of the application that contains the fragments to remove. internal void Remove(IApplicationContext applicationContext) { - foreach (var settingPageContext in _dictionary.Remove(applicationContext)) + foreach (var settingPageContext in _pageDictionary.Remove(applicationContext)) { OnRemoveSettingPage(settingPageContext); } @@ -353,61 +585,72 @@ internal void Remove(IApplicationContext applicationContext) /// An enumeration of setting page contextes. public IEnumerable GetSettingPages(Type settingPageType) { - return _dictionary.GetSettingPages(settingPageType); + return _pageDictionary.GetSettingPages(settingPageType); } /// - /// Returns an enumeration of setting page contextes. + /// Returns an enumeration of setting page contexts. /// - /// The setting page type. - /// The context of the application. - /// An enumeration of setting page contextes. + /// The type of the setting page. + /// The application context in which the setting pages are retrieved. + /// An enumeration of setting page contexts. public IEnumerable GetSettingPages(Type settingPageType, IApplicationContext applicationContext) { - return _dictionary.GetSettingPages(settingPageType, applicationContext); + return _pageDictionary.GetSettingPages(settingPageType, applicationContext); } /// - /// Returns the categories associated with the specified application context. + /// Returns the category contexts associated with the given application context. /// - /// The context of the application. - /// An enumeration of category names. - public IEnumerable GetCategories(IApplicationContext applicationContext) + /// The application context for which the categories are to be retrieved. + /// An enumeration of category contexts associated with the specified application context. + public IEnumerable GetSettingCategories(IApplicationContext applicationContext) { - return _dictionary.GetCategories(applicationContext); + return _categoryDictionary.GetSettingCategories(applicationContext); } /// - /// Returns the groups associated with the specified application context and category. + /// Returns the setting groups associated with the specified application context and category. /// - /// The context of the application. - /// The category for which to retrieve groups. - /// An enumeration of group names. - public IEnumerable GetGroups(IApplicationContext applicationContext, string category) + /// The application context used to identify the relevant groups. + /// The category context for filtering the setting groups. + /// An enumeration of setting group contexts associated with the provided application context and category. + public IEnumerable GetSettingGroups(IApplicationContext applicationContext, ISettingCategoryContext categoryContext) { - return _dictionary.GetGroups(applicationContext, category); + return _groupDictionary.GetSettingGroups(applicationContext, categoryContext); } /// /// Returns an enumeration of setting page contexts for the specified application context and category. /// /// The context of the application. - /// The category for which to retrieve setting pages. + /// The category for which to retrieve setting pages. /// An enumeration of setting page contexts. - public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category) + public IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingCategoryContext categoryContext) { - return _dictionary.GetSettingPages(applicationContext, category); + return _pageDictionary.GetSettingPages(applicationContext, categoryContext); + } + + /// + /// Returns an enumeration of setting page contexts for the specified application context, category, and group. + /// + /// The context of the application. + /// The group for which to retrieve setting pages. + /// An enumeration of setting page contexts. + public IEnumerable GetSettingPages(IApplicationContext applicationContext, ISettingGroupContext groupContext) + { + return _pageDictionary.GetSettingPages(applicationContext, groupContext); } /// /// Returns the first setting page context for the specified application context and category. /// /// The context of the application. - /// The category for which to retrieve setting pages. + /// The category for which to retrieve setting pages. /// The first setting page context or null. - public ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, string category) + public ISettingPageContext GetFirstSettingPage(IApplicationContext applicationContext, ISettingCategoryContext categoryContext) { - var pages = _dictionary.GetSettingPages(applicationContext, category); + var pages = _pageDictionary.GetSettingPages(applicationContext, categoryContext); var preferences = pages.Where(x => x.Section == SettingSection.Preferences); var primary = pages.Where(x => x.Section == SettingSection.Primary); var secondary = pages.Where(x => x.Section == SettingSection.Secondary); @@ -430,18 +673,6 @@ public ISettingPageContext GetFirstSettingPage(IApplicationContext applicationCo return null; } - /// - /// Returns an enumeration of setting page contexts for the specified application context, category, and group. - /// - /// The context of the application. - /// The category for which to retrieve setting pages. - /// The group for which to retrieve setting pages. - /// An enumeration of setting page contexts. - public IEnumerable GetSettingPages(IApplicationContext applicationContext, string category, string group) - { - return _dictionary.GetSettingPages(applicationContext, category, group); - } - /// /// Raises the AddSettingPage event. /// @@ -460,6 +691,42 @@ private void OnRemoveSettingPage(ISettingPageContext settingPageContext) RemoveSettingPage?.Invoke(this, settingPageContext); } + /// + /// Raises the AddSettingCategory event. + /// + /// The setting category context. + private void OnAddSettingCategory(ISettingCategoryContext settingCategoryeContext) + { + AddSettingCategory?.Invoke(this, settingCategoryeContext); + } + + /// + /// Raises the RemoveSettingCategory event. + /// + /// The setting category context. + private void OnRemoveSettingCategory(ISettingCategoryContext settingCategoryContext) + { + RemoveSettingCategory?.Invoke(this, settingCategoryContext); + } + + /// + /// Raises the AddSettingGroup event. + /// + /// The setting group context. + private void OnAddSettingGroup(ISettingGroupContext settingGroupContext) + { + AddSettingGroup?.Invoke(this, settingGroupContext); + } + + /// + /// Raises the RemoveSettingGroup event. + /// + /// The setting group context. + private void OnRemoveSettingGroup(ISettingGroupContext settingGroupContext) + { + RemoveSettingGroup?.Invoke(this, settingGroupContext); + } + /// /// Raises the event when an plugin is added. /// From d3d273ec32445c7c943a9b044791f32fce8b30fa Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 9 Mar 2025 09:48:03 +0100 Subject: [PATCH 133/162] feat: enhance the settings page manager --- src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs | 2 +- src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 93c21f1..9f3258f 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -20,7 +20,7 @@ public class SettingPageContext : PageContext, ISettingPageContext /// /// Returns the setting category context to which the setting page belongs. /// - public ISettingCategoryContext SettingCategory { get; internal set; } + public ISettingCategoryContext SettingCategory => SettingGroup?.SettingCategory; /// /// Returns the group context to which the setting page belongs. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index c2a2e09..ccbdbb8 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -518,7 +518,6 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable Date: Sat, 15 Mar 2025 09:25:48 +0100 Subject: [PATCH 134/162] bug fixes --- src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj | 6 +++--- src/WebExpress.WebCore/WebAsset/Asset.cs | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index 6d63af8..5c66e53 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -58,9 +58,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/WebExpress.WebCore/WebAsset/Asset.cs b/src/WebExpress.WebCore/WebAsset/Asset.cs index 3487aa8..e554e83 100644 --- a/src/WebExpress.WebCore/WebAsset/Asset.cs +++ b/src/WebExpress.WebCore/WebAsset/Asset.cs @@ -2,11 +2,10 @@ using System.IO; using System.Reflection; using WebExpress.WebCore.Internationalization; -using WebExpress.WebCore.WebAsset; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebMessage; -namespace WebExpress.WebCore.WebResource +namespace WebExpress.WebCore.WebAsset { /// /// Delivery of a resource embedded in the assembly. From b2f7b35ffbb223d0141f16218f6dd109fde58c4a Mon Sep 17 00:00:00 2001 From: ReneSchwarzer Date: Sat, 15 Mar 2025 11:43:31 +0100 Subject: [PATCH 135/162] refactor: optimize initialization --- .../Fixture/UnitTestFixture.cs | 5 +- .../WebPage/Model/PageItem.cs | 68 +++++++++- src/WebExpress.WebCore/WebPage/PageContext.cs | 39 +----- src/WebExpress.WebCore/WebPage/PageManager.cs | 40 +++--- .../WebSettingPage/Model/SettingPageItem.cs | 117 +++++++++++++++++- .../WebSettingPage/SettingPageContext.cs | 13 +- .../WebSettingPage/SettingPageManager.cs | 39 +++--- .../WebStatusPage/StatusPageManager.cs | 2 +- 8 files changed, 224 insertions(+), 99 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index 5c66546..3f1d031 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -6,7 +6,6 @@ using System.Text; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; @@ -200,9 +199,9 @@ public static RenderContext CrerateRenderContextMock(IApplicationContext applica /// A fake context for testing. public static PageContext CreratePageContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) { - var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(IEndpointManager), typeof(Type), typeof(UriResource), typeof(IUriPathSegment)], null); + var ctorPageContext = typeof(PageContext).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [], null); - var pageContext = (PageContext)ctorPageContext.Invoke([WebEx.ComponentHub.EndpointManager, null, new UriResource(), null]); + var pageContext = (PageContext)ctorPageContext.Invoke([]); pageContext.ApplicationContext = applicationContext; pageContext.Scopes = scopes; diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 60d673f..0b5d3fa 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -1,7 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage.Model @@ -11,7 +15,23 @@ namespace WebExpress.WebCore.WebPage.Model /// internal class PageItem : IDisposable { - private readonly IPageManager _pageManager; + private readonly IEndpointManager _endpointManager; + private PageContext _pageContext; + + /// + /// Returns the endpoint id. + /// + public IComponentId EndpointId { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns or sets the resource title. @@ -70,18 +90,54 @@ internal class PageItem : IDisposable /// public bool Optional { get; set; } + /// + /// Returns the attributes associated with the page. + /// + public IEnumerable Attributes { get; internal set; } + /// /// Returns the page context. /// - public IPageContext PageContext { get; internal set; } + public IPageContext PageContext + { + get + { + _pageContext ??= new PageContext() + { + PageTitle = Title, + EndpointId = EndpointId, + PluginContext = PluginContext, + ApplicationContext = ApplicationContext, + Cache = Cache, + Scopes = Scopes, + Conditions = Conditions, + IncludeSubPaths = IncludeSubPaths, + Attributes = Attributes, + }; + + var parentContext = _endpointManager.GetEndpoints(ParentType, ApplicationContext) + .FirstOrDefault(); + + var contextPath = UriResource.Combine + ( + parentContext?.Uri ?? ApplicationContext?.ContextPath, ContextPath + ); + + _pageContext.ParentContext = parentContext; + _pageContext.ContextPath = contextPath; + _pageContext.Uri = contextPath.Append(PathSegment); + + return _pageContext; + } + } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The page manager. - internal PageItem(IPageManager pageManager) + /// The endpoint manager responsible for managing endpoints. + internal PageItem(IEndpointManager endpointManager) { - _pageManager = pageManager; + _endpointManager = endpointManager; } /// diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index f270b6f..7312062 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -15,11 +14,6 @@ namespace WebExpress.WebCore.WebPage /// public class PageContext : IPageContext { - private readonly IEndpointManager _endpointManager; - private readonly Type _parentType; - private readonly UriResource _contextPath; - private readonly IUriPathSegment _pathSegment; - /// /// Returns the associated plugin context. /// @@ -48,15 +42,14 @@ public class PageContext : IPageContext public IComponentId EndpointId { get; internal set; } /// - /// Returns the resource title. + /// Returns the page title. /// public string PageTitle { get; internal set; } /// /// Returns the parent or null if not used. /// - public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) - .FirstOrDefault(); + public IEndpointContext ParentContext { get; internal set; } /// /// Returns whether the resource is created once and reused each time it is called. @@ -76,38 +69,18 @@ public class PageContext : IPageContext /// /// Returns the context path. /// - public UriResource ContextPath - { - get - { - var parentContext = ParentContext; - if (parentContext != null) - { - return UriResource.Combine(ParentContext?.Uri, _contextPath); - } - - return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); - } - } + public UriResource ContextPath { get; internal set; } /// /// Returns the uri. /// - public UriResource Uri => ContextPath.Append(_pathSegment); + public UriResource Uri { get; internal set; } /// - /// Initializes a new instance of the class with the specified parent type and context path. + /// Initializes a new instance of the class. /// - /// The endpoint manager responsible for managing endpoints. - /// The type of the parent resource. - /// The context path of the resource. - /// The path segment of the resource. - public PageContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) + public PageContext() { - _endpointManager = endpointManager; - _parentType = parentType; - _contextPath = contextPath; - _pathSegment = pathSegment; } /// diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index c62888a..7b7c73c 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -296,29 +296,29 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IPage<>).Name) != null)) { - var id = resourceType.FullName?.ToLower(); + var id = pageType.FullName?.ToLower(); var segment = default(ISegmentAttribute); - var title = resourceType.Name; + var title = pageType.Name; var parent = default(Type); var contextPath = string.Empty; var includeSubPaths = false; var scopes = new List(); var conditions = new List(); var cache = false; - var attributes = resourceType.CustomAttributes + var attributes = pageType.CustomAttributes .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); - foreach (var customAttribute in resourceType.CustomAttributes + foreach (var customAttribute in pageType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { - segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; + segment = pageType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { @@ -343,7 +343,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute)))) { if (customAttribute.AttributeType == typeof(TitleAttribute)) @@ -356,44 +356,34 @@ private void Register(IPluginContext pluginContext, IEnumerable x == typeof(IScope)).Any()) + if (pageType.GetInterfaces().Where(x => x == typeof(IScope)).Any()) { - scopes.Add(resourceType); + scopes.Add(pageType); } // assign the page to existing applications foreach (var applicationContext in applicationContexts) { - var pageContext = new PageContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) + var pageItem = new PageItem(_componentHub.EndpointManager) { - PageTitle = title, - EndpointId = new ComponentId(resourceType.FullName), + EndpointId = new ComponentId(id), PluginContext = pluginContext, ApplicationContext = applicationContext, - Cache = cache, - Scopes = scopes, - Conditions = conditions, - IncludeSubPaths = includeSubPaths, - Attributes = attributes.Select(x => x.AttributeType) - }; - - var pageItem = new PageItem(_componentHub.PageManager) - { Title = title, - PageContext = pageContext, ParentType = parent, - PageClass = resourceType, + PageClass = pageType, Scopes = scopes, Cache = cache, Conditions = conditions, ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, - PathSegment = segment.ToPathSegment() + PathSegment = segment.ToPathSegment(), + Attributes = attributes.Select(x => x.AttributeType) }; if (_dictionary.AddPageItem(pluginContext, applicationContext, pageItem)) { - OnAddPage(pageContext); + OnAddPage(pageItem.PageContext); _httpServerContext?.Log.Debug ( diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index d5927bc..d71fb2f 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -1,7 +1,12 @@ using System; +using System.Collections.Generic; +using System.Linq; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSettingPage.Model { @@ -10,6 +15,14 @@ namespace WebExpress.WebCore.WebSettingPage.Model /// public class SettingPageItem : IDisposable { + private readonly IEndpointManager _endpointManager; + private SettingPageContext _settingPageContext; + + /// + /// Returns the endpoint id. + /// + public IComponentId EndpointId { get; internal set; } + /// /// Returns the context of the associated plugin. /// @@ -23,7 +36,39 @@ public class SettingPageItem : IDisposable /// /// Returns the setting page context. /// - public ISettingPageContext SettingPageContext { get; internal set; } + public ISettingPageContext SettingPageContext + { + get + { + _settingPageContext ??= new SettingPageContext() + { + PageTitle = PageTitle, + EndpointId = EndpointId, + PluginContext = PluginContext, + ApplicationContext = ApplicationContext, + Cache = Cache, + Scopes = Scopes, + Conditions = Conditions, + IncludeSubPaths = IncludeSubPaths, + Attributes = Attributes, + SettingGroup = SettingGroup, + }; + + var parentContext = _endpointManager.GetEndpoints(ParentType, ApplicationContext) + .FirstOrDefault(); + + var contextPath = UriResource.Combine + ( + parentContext?.Uri ?? ApplicationContext.ContextPath, ContextPath + ); + + _settingPageContext.ParentContext = parentContext; + _settingPageContext.ContextPath = contextPath; + _settingPageContext.Uri = contextPath.Append(PathSegment); + + return _settingPageContext; + } + } /// /// Returns the class type of the setting page. @@ -36,9 +81,24 @@ public class SettingPageItem : IDisposable public IEndpoint Instance { get; internal set; } /// - /// Returns the group. + /// Returns or sets the parent type. + /// + public Type ParentType { get; set; } + + /// + /// Returns or sets the paths of the resource. + /// + public UriResource ContextPath { get; set; } + + /// + /// Returns or sets the path segment. + /// + public IUriPathSegment PathSegment { get; internal set; } + + /// + /// Returns the group type. /// - public Type Group { get; internal set; } + public Type SettingGroupType { get; internal set; } /// /// Returns the section. @@ -50,6 +110,57 @@ public class SettingPageItem : IDisposable /// public bool Cache { get; internal set; } + /// + /// Returns the icon. + /// + public string Icon { get; internal set; } + + /// + /// Returns the setting page title. + /// + public string PageTitle { get; internal set; } + + /// + /// Returns a value indicating whether the page should be displayed or hidden. + /// + public bool Hide { get; internal set; } + + /// + /// Returns or sets whether all subpaths should be taken into sitemap. + /// + public bool IncludeSubPaths { get; internal set; } + + /// + /// Returns the attributes associated with the page. + /// + public IEnumerable Attributes { get; internal set; } + + /// + /// Returns the scope names that provides the resource. The scope name + /// is a string with a name (e.g. global, admin), which can be used by elements to + /// determine whether content and how content should be displayed. + /// + public IEnumerable Scopes { get; internal set; } = []; + + /// + /// Returns the conditions that must be met for the resource to be active. + /// + public IEnumerable Conditions { get; internal set; } = []; + + /// + /// Returns the group context to which the setting page belongs. + /// + public ISettingGroupContext SettingGroup { get; internal set; } + + /// + /// Initializes a new instance of the class. + /// + /// The endpoint manager responsible for managing endpoints. + internal SettingPageItem(IEndpointManager endpointManager) + { + _endpointManager = endpointManager; + } + /// /// Release of unmanaged resources reserved during use. /// diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 9f3258f..64e12ab 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -1,8 +1,5 @@ -using System; -using WebExpress.WebCore.WebEndpoint; -using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage.Model; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSettingPage { @@ -40,12 +37,8 @@ public class SettingPageContext : PageContext, ISettingPageContext /// /// Initializes a new instance of the class with the specified parent type and context path. /// - /// The endpoint manager responsible for managing endpoints. - /// The type of the parent resource. - /// The context path of the resource. - /// The path segment of the resource. - public SettingPageContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) - : base(endpointManager, parentType, contextPath, pathSegment) + public SettingPageContext() + : base() { } } diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index ccbdbb8..7809d90 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -436,9 +436,13 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable(); var group = default(Type); var section = SettingSection.Primary; + var includeSubPaths = false; var hide = false; var icon = default(string); var cache = false; + var attributes = settingPageType.CustomAttributes + .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && + !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); // determining attributes foreach (var customAttribute in settingPageType.CustomAttributes @@ -476,6 +480,10 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable x.AttributeType) }; // insert the settings page into the dictionary if (_pageDictionary.AddSettingPageItem(settingPageItem)) { - OnAddSettingPage(settingPageContext); + OnAddSettingPage(settingPageItem.SettingPageContext); _httpServerContext?.Log.Debug ( diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 9c6ffb2..0658ddf 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -297,7 +297,7 @@ public Response CreateStatusResponse(string message, int status, IApplicationCon new StatusMessage(message) ); var pageType = pageInstance.GetType(); - var pageContext = new PageContext(_componentHub.EndpointManager, null, request.Uri, new UriPathSegmentRoot()); + var pageContext = new PageContext(); var renderContext = new RenderContext(pageInstance as IEndpoint, pageContext, request); var visualTreeContext = new VisualTreeContext(renderContext); From 9ec0de461435e5efa8ce751c7f9134587ebc8ffd Mon Sep 17 00:00:00 2001 From: ReneSchwarzer Date: Sat, 15 Mar 2025 13:46:15 +0100 Subject: [PATCH 136/162] feat: derive paths from namespaces unless explicitly defined --- .../Manager/UnitTestPageManager.cs | 8 +-- .../Manager/UnitTestSitemapManager.cs | 18 ++++--- src/WebExpress.WebCore.Test/TestPageB.cs | 3 +- src/WebExpress.WebCore.Test/TestPageC.cs | 1 - .../Web/Home/TestPageC.cs | 52 +++++++++++++++++++ src/WebExpress.WebCore/WebPage/PageManager.cs | 11 +++- .../WebResource/ResourceManager.cs | 11 +++- .../WebRestAPI/RestApiManager.cs | 27 ++++++---- .../WebSettingPage/SettingPageManager.cs | 11 +++- 9 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index 0e654a3..09e71ac 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -20,7 +20,7 @@ public void Register() var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(9, componentHub.PageManager.Pages.Count()); + Assert.Equal(12, componentHub.PageManager.Pages.Count()); } /// @@ -94,13 +94,13 @@ public void Title(Type applicationType, Type resourceType, string title) /// [Theory] [InlineData(typeof(TestApplicationA), typeof(TestPageA), "/server/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/server/appa/resa")] + [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/server/appa/resa/b")] [InlineData(typeof(TestApplicationA), typeof(TestPageC), "/server/appa")] [InlineData(typeof(TestApplicationB), typeof(TestPageA), "/server/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/server/appb/resa")] + [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/server/appb/resa/b")] [InlineData(typeof(TestApplicationB), typeof(TestPageC), "/server/appb")] [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/server")] - [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/server/resa")] + [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/server/resa/b")] [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/server")] public void ContextPath(Type applicationType, Type resourceType, string path) { diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index c7df0eb..59d5a25 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.Web.Home; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebUri; @@ -23,7 +24,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(46, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(58, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -43,13 +44,13 @@ public void Refresh() [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appa/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appa/resa/B/testpageb", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appb/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appb/resa/b/testpageb", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/resa/b/TestPageB", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] @@ -93,8 +94,9 @@ public void SearchResource(string uri, string id) [InlineData(typeof(TestResourceC), "/server/appa/resc")] [InlineData(typeof(TestResourceD), "/server/appa/resd")] [InlineData(typeof(TestPageA), "/server/appa/pagea")] - [InlineData(typeof(TestPageB), "/server/appa/resa/pageb")] + [InlineData(typeof(TestPageB), "/server/appa/resa/b/testpageb")] [InlineData(typeof(TestPageC), "/server")] + [InlineData(typeof(TestPageH), "/server/appa/web/home/testpageh")] public void GetUri(Type resourceType, string expected) { // preconditions @@ -124,13 +126,13 @@ public void GetUri(Type resourceType, string expected) [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appa/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appa/resa/B/testpageb", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appb/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/appb/resa/b/TestPageB", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/resa/pageb", "webexpress.webcore.test.testpageb")] + [InlineData("http://localhost:8080/server/resa/b/testpageb", "webexpress.webcore.test.testpageb")] [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] diff --git a/src/WebExpress.WebCore.Test/TestPageB.cs b/src/WebExpress.WebCore.Test/TestPageB.cs index 189796d..dec02fd 100644 --- a/src/WebExpress.WebCore.Test/TestPageB.cs +++ b/src/WebExpress.WebCore.Test/TestPageB.cs @@ -8,9 +8,8 @@ namespace WebExpress.WebCore.Test /// A dummy class for testing purposes. /// [Title("webindex:pageb.label")] - [Segment("pageb", "webindex:homepage.label")] - [ContextPath(null)] [Parent] + [ContextPath("/b")] public sealed class TestPageB : IPage, IScope { /// diff --git a/src/WebExpress.WebCore.Test/TestPageC.cs b/src/WebExpress.WebCore.Test/TestPageC.cs index 9c66427..d722b11 100644 --- a/src/WebExpress.WebCore.Test/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/TestPageC.cs @@ -8,7 +8,6 @@ namespace WebExpress.WebCore.Test /// [Title("webindex:pagec.label")] [Segment(null, "webindex:homepage.label")] - [ContextPath(null)] public sealed class TestPageC : Page { /// diff --git a/src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs b/src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs new file mode 100644 index 0000000..4d444f5 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs @@ -0,0 +1,52 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.Web.Home +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:pagec.label")] + public sealed class TestPageH : Page + { + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + private TestPageH(IPageContext pageContext) + { + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public override void Process(IRenderContext renderContext, TestVisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + + // test the visualTree + if (visualTree == null) + { + throw new ArgumentNullException(nameof(visualTree), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public override void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 7b7c73c..99116cc 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -361,6 +361,15 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType) }; diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index d6d6d8b..4a51f3d 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -166,6 +166,15 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IRestApi).Name) != null)) { - var id = resourceType.FullName?.ToLower(); + var id = resrApiType.FullName?.ToLower(); var segment = default(ISegmentAttribute); - var title = resourceType.Name; + var title = resrApiType.Name; var parent = default(Type); var contextPath = string.Empty; var includeSubPaths = false; @@ -318,12 +318,12 @@ private void Register(IPluginContext pluginContext, IEnumerable(); var version = 1u; - foreach (var customAttribute in resourceType.CustomAttributes + foreach (var customAttribute in resrApiType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { - segment = resourceType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; + segment = resrApiType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) { @@ -353,7 +353,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IRestApiAttribute)))) { if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) @@ -363,12 +363,21 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType) }; From e03868af62fd438d77e9a3e1543774dc48633e0b Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 15 Mar 2025 17:36:39 +0100 Subject: [PATCH 137/162] bug fixes --- .../Manager/UnitTestSettingPageManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 4164165..a3786f4 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -280,12 +280,12 @@ public void GroupSection(Type applicationType, Type settingCategoryType, params /// Test the category property of the setting groups. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] - [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] - [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA), new[] { SettingSection.Preferences, SettingSection.Primary })] - [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB), new SettingSection[0])] - [InlineData(typeof(TestApplicationA), null, new[] { SettingSection.Secondary })] - public void GroupCategory(Type applicationType, Type settingCategoryType, params SettingSection[] sections) + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryA))] + [InlineData(typeof(TestApplicationB), typeof(TestSettingCategoryA))] + [InlineData(typeof(TestApplicationC), typeof(TestSettingCategoryA))] + [InlineData(typeof(TestApplicationA), typeof(TestSettingCategoryB))] + [InlineData(typeof(TestApplicationA), null)] + public void GroupCategory(Type applicationType, Type settingCategoryType) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); From c1550a75ef81bc9a78f45cacc73f1d7c3914e26d Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 16 Mar 2025 00:49:36 +0100 Subject: [PATCH 138/162] add: support for embedding icons in html --- .../Manager/UnitTestSettingPageManager.cs | 18 ++++++++++++ src/WebExpress.WebCore.Test/TestIconBell.cs | 23 +++++++++++++++ .../TestIconPalette.cs | 23 +++++++++++++++ .../TestIconProfile.cs | 23 +++++++++++++++ src/WebExpress.WebCore.Test/TestIconShild.cs | 24 ++++++++++++++++ src/WebExpress.WebCore.Test/TestIconTool.cs | 24 ++++++++++++++++ src/WebExpress.WebCore.Test/TestIconWrench.cs | 24 ++++++++++++++++ .../TestSettingCategoryA.cs | 2 +- .../TestSettingCategoryB.cs | 2 +- .../TestSettingCategoryC.cs | 1 - .../TestSettingGroupA.cs | 2 +- .../TestSettingGroupB.cs | 2 +- .../TestSettingGroupC.cs | 1 - .../TestSettingPageA.cs | 1 + .../TestSettingPageB.cs | 3 +- .../WebAttribute/CacheAttribute.cs | 2 +- .../WebAttribute/IconAttribute.cs | 2 +- .../WebAttribute/WebIconAttribute.cs | 22 +++++++++++++++ src/WebExpress.WebCore/WebIcon/IIcon.cs | 19 +++++++++++++ .../WebSettingPage/ISettingCategoryContext.cs | 3 +- .../WebSettingPage/ISettingGroupContext.cs | 3 +- .../WebSettingPage/ISettingPageContext.cs | 5 ++-- .../WebSettingPage/Model/SettingPageItem.cs | 4 ++- .../WebSettingPage/SettingCategoryContext.cs | 3 +- .../WebSettingPage/SettingGroupContext.cs | 3 +- .../WebSettingPage/SettingPageContext.cs | 5 ++-- .../WebSettingPage/SettingPageManager.cs | 28 +++++++++++-------- 27 files changed, 242 insertions(+), 30 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/TestIconBell.cs create mode 100644 src/WebExpress.WebCore.Test/TestIconPalette.cs create mode 100644 src/WebExpress.WebCore.Test/TestIconProfile.cs create mode 100644 src/WebExpress.WebCore.Test/TestIconShild.cs create mode 100644 src/WebExpress.WebCore.Test/TestIconTool.cs create mode 100644 src/WebExpress.WebCore.Test/TestIconWrench.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/WebIconAttribute.cs create mode 100644 src/WebExpress.WebCore/WebIcon/IIcon.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index a3786f4..56a1bf0 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -177,6 +177,24 @@ public void CategoryName(Type applicationType, params string[] names) Assert.Equal([.. names], [.. settingCategories.Select(x => x.Name)]); } + /// + /// Test the icon property of the setting categories. + /// + [Theory] + [InlineData(typeof(TestApplicationA), new[] { "WebExpress.WebCore.Test.TestIconBell", "WebExpress.WebCore.Test.TestIconProfile", null })] + [InlineData(typeof(TestApplicationB), new[] { "WebExpress.WebCore.Test.TestIconBell", "WebExpress.WebCore.Test.TestIconProfile", null })] + [InlineData(typeof(TestApplicationC), new[] { "WebExpress.WebCore.Test.TestIconBell", "WebExpress.WebCore.Test.TestIconProfile", null })] + public void CategoryIcon(Type applicationType, params string[] icons) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); + var settingCategories = componentHub.SettingPageManager.GetSettingCategories(application); + + // test execution + Assert.Equal([.. icons], [.. settingCategories.Select(x => x.Icon?.ToString())]); + } + /// /// Test the description property of the setting categories. /// diff --git a/src/WebExpress.WebCore.Test/TestIconBell.cs b/src/WebExpress.WebCore.Test/TestIconBell.cs new file mode 100644 index 0000000..b42172f --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconBell.cs @@ -0,0 +1,23 @@ +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconBell : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("🔔")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIconPalette.cs b/src/WebExpress.WebCore.Test/TestIconPalette.cs new file mode 100644 index 0000000..16ed54a --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconPalette.cs @@ -0,0 +1,23 @@ +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconPalette : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("🎨")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIconProfile.cs b/src/WebExpress.WebCore.Test/TestIconProfile.cs new file mode 100644 index 0000000..acf02b2 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconProfile.cs @@ -0,0 +1,23 @@ +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconProfile : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("👤")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIconShild.cs b/src/WebExpress.WebCore.Test/TestIconShild.cs new file mode 100644 index 0000000..73dc193 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconShild.cs @@ -0,0 +1,24 @@ + +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconShild : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("🛡")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIconTool.cs b/src/WebExpress.WebCore.Test/TestIconTool.cs new file mode 100644 index 0000000..2c36e89 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconTool.cs @@ -0,0 +1,24 @@ + +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconTool : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("🛠")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestIconWrench.cs b/src/WebExpress.WebCore.Test/TestIconWrench.cs new file mode 100644 index 0000000..43353fb --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestIconWrench.cs @@ -0,0 +1,24 @@ + +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test icon that can be rendered to HTML. + /// + public class TestIconWrench : IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + { + return new HtmlElementTextSemanticsSpan(new HtmlText("🔧")); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs index 3e18486..97a4499 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting category for testing purposes. /// - [Icon("InfoCircle")] + [WebIcon] [Name("SettingCategory A")] [Description("Description of category a.")] [SettingSection(SettingSection.Preferences)] diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs index 8990581..2bf328f 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting category for testing purposes. /// - [Icon("InfoCircle")] + [WebIcon] [Name("SettingCategory B")] [Description("Description of category b.")] [SettingSection(SettingSection.Primary)] diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs index bb2f910..99f344e 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs @@ -7,7 +7,6 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting category for testing purposes. /// - [Icon("InfoCircle")] [Name("SettingCategory C")] [Description("Description of category c.")] [SettingSection(SettingSection.Secondary)] diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs index b92229d..57ef17f 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting group for testing purposes. /// - [Icon("InfoCircle")] + [WebIcon] [Name("SettingGroup A")] [Description("Description of group a.")] [SettingCategory()] diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs index 2d22821..b349a19 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting group for testing purposes. /// - [Icon("InfoCircle")] + [WebIcon()] [Name("SettingGroup B")] [Description("Description of group b.")] [SettingCategory()] diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs index e6d53de..d62c742 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs @@ -7,7 +7,6 @@ namespace WebExpress.WebCore.Test /// /// A dummy setting group for testing purposes. /// - [Icon("InfoCircle")] [Name("SettingGroup C")] [Description("Description of group c.")] [SettingSection(SettingSection.Secondary)] diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/TestSettingPageA.cs index b112428..86da58f 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageA.cs @@ -7,6 +7,7 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// + [WebIcon] [Title("webindex:settingpagea.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/TestSettingPageB.cs index 71ab34d..73733ae 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingPageB.cs @@ -7,10 +7,11 @@ namespace WebExpress.WebCore.Test /// /// A dummy class for testing purposes. /// + [WebIcon] [Title("webindex:settingpageb.label")] [Segment("settingpagea", "webindex:homepage.label")] [ContextPath(null)] - [SettingGroup()] + [SettingGroup] public sealed class TestSettingPageB : ISettingPage { /// diff --git a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs index 707aeb6..e90b6ab 100644 --- a/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/CacheAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Indicates that a page or component can be reused /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class CacheAttribute : System.Attribute, IEndpointAttribute + public class CacheAttribute : Attribute, IEndpointAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs index 1b228bd..7f8643f 100644 --- a/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/IconAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to specify an icon for a plugin, application, or status page. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class IconAttribute : Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute, ISettingPageAttribute, ISettingCategoryAttribute, ISettingGroupAttribute + public class IconAttribute : Attribute, IPluginAttribute, IApplicationAttribute, IStatusPageAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/WebIconAttribute.cs b/src/WebExpress.WebCore/WebAttribute/WebIconAttribute.cs new file mode 100644 index 0000000..a110a65 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/WebIconAttribute.cs @@ -0,0 +1,22 @@ +using System; +using WebExpress.WebCore.WebIcon; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify an icon for a plugin, application, or status page. + /// + /// The type of the icon, which must implement the interface. + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class WebIconAttribute : Attribute, ISettingPageAttribute, ISettingCategoryAttribute, ISettingGroupAttribute + where TIcon : IIcon + { + /// + /// Initializes a new instance of the class. + /// + public WebIconAttribute() + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebIcon/IIcon.cs b/src/WebExpress.WebCore/WebIcon/IIcon.cs new file mode 100644 index 0000000..624cb5c --- /dev/null +++ b/src/WebExpress.WebCore/WebIcon/IIcon.cs @@ -0,0 +1,19 @@ +using WebExpress.WebCore.WebHtml; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.WebIcon +{ + /// + /// Represents an icon that can be rendered to HTML. + /// + public interface IIcon + { + /// + /// Converts the icon to an HTML representation. + /// + /// The context in which the icon is rendered. + /// The visual tree representing the icon's structure. + /// An HTML node representing the rendered icon. + IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree); + } +} diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs index 3e56bc8..c897ae3 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSettingPage.Model; @@ -28,7 +29,7 @@ public interface ISettingCategoryContext : IContext /// /// Returns the icon. /// - string Icon { get; } + IIcon Icon { get; } /// /// Returns the name. diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs index f7ef773..52abfdb 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSettingPage.Model; @@ -33,7 +34,7 @@ public interface ISettingGroupContext : IContext /// /// Returns the icon. /// - string Icon { get; } + IIcon Icon { get; } /// /// Returns the name. diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index 633f65d..d685627 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage @@ -12,7 +13,7 @@ public interface ISettingPageContext : IPageContext /// /// Returns the icon. /// - string Icon { get; } + IIcon Icon { get; } /// /// Returns the setting category context to which the setting page belongs. diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index d71fb2f..2292b13 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -5,6 +5,7 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; @@ -52,6 +53,7 @@ public ISettingPageContext SettingPageContext IncludeSubPaths = IncludeSubPaths, Attributes = Attributes, SettingGroup = SettingGroup, + Icon = Icon, }; var parentContext = _endpointManager.GetEndpoints(ParentType, ApplicationContext) @@ -113,7 +115,7 @@ public ISettingPageContext SettingPageContext /// /// Returns the icon. /// - public string Icon { get; internal set; } + public IIcon Icon { get; internal set; } /// /// Returns the setting page title. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs index cdc042a..18ac99d 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSettingPage.Model; @@ -28,7 +29,7 @@ public class SettingCategoryContext : ISettingCategoryContext /// /// Returns the icon. /// - public string Icon { get; internal set; } + public IIcon Icon { get; internal set; } /// /// Returns the name. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs index d588799..918c557 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebSettingPage.Model; @@ -33,7 +34,7 @@ public class SettingGroupContext : ISettingGroupContext /// /// Returns the icon. /// - public string Icon { get; internal set; } + public IIcon Icon { get; internal set; } /// /// Returns the name. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index 64e12ab..ccb114c 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebPage; +using WebExpress.WebCore.WebIcon; +using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage @@ -12,7 +13,7 @@ public class SettingPageContext : PageContext, ISettingPageContext /// /// Returns the icon. /// - public string Icon { get; internal set; } + public IIcon Icon { get; internal set; } /// /// Returns the setting category context to which the setting page belongs. diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index 52bda63..be64a45 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -9,6 +9,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; @@ -238,7 +239,7 @@ private void RegisterCategory(IPluginContext pluginContext, IEnumerable x.GetInterface(typeof(ISettingCategory).Name) != null)) { var id = settingCategoryType.FullName?.ToLower(); - var icon = default(string); + var icon = default(IIcon); var name = default(string); var description = default(string); var section = SettingSection.Primary; @@ -247,17 +248,18 @@ private void RegisterCategory(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(ISettingCategoryAttribute)))) { - if (customAttribute.AttributeType == typeof(IconAttribute)) + if (customAttribute.AttributeType.IsGenericType && customAttribute.AttributeType.GetGenericTypeDefinition() == typeof(WebIconAttribute<>)) { - icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + icon ??= Activator.CreateInstance(type) as IIcon; } else if (customAttribute.AttributeType == typeof(NameAttribute)) { - name = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + name ??= customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } else if (customAttribute.AttributeType == typeof(DescriptionAttribute)) { - description = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + description ??= customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); } else if (customAttribute.AttributeType == typeof(SettingSectionAttribute)) { @@ -324,7 +326,7 @@ private void RegisterGroup(IPluginContext pluginContext, IEnumerable x.GetInterface(typeof(ISettingGroup).Name) != null)) { var id = settingGroupType.FullName?.ToLower(); - var icon = default(string); + var icon = default(IIcon); var name = default(string); var description = default(string); var category = default(Type); @@ -334,9 +336,10 @@ private void RegisterGroup(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(ISettingGroupAttribute)))) { - if (customAttribute.AttributeType == typeof(IconAttribute)) + if (customAttribute.AttributeType.IsGenericType && customAttribute.AttributeType.GetGenericTypeDefinition() == typeof(WebIconAttribute<>)) { - icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + icon ??= Activator.CreateInstance(type) as IIcon; } else if (customAttribute.AttributeType == typeof(NameAttribute)) { @@ -438,7 +441,7 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && @@ -452,7 +455,7 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable)) + else if (customAttribute.AttributeType.IsGenericType && customAttribute.AttributeType.GetGenericTypeDefinition() == typeof(ParentAttribute<>)) { parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); } @@ -472,9 +475,10 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable)) { - icon = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + var type = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + icon ??= Activator.CreateInstance(type) as IIcon; } else if (customAttribute.AttributeType == typeof(CacheAttribute)) { From 9dc464444491f03674a26ced0d062e32c7499a70 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 16 Mar 2025 12:19:01 +0100 Subject: [PATCH 139/162] feat: improve web icons --- src/WebExpress.WebCore.Test/TestIconBell.cs | 7 ++++++- src/WebExpress.WebCore.Test/TestIconPalette.cs | 7 ++++++- src/WebExpress.WebCore.Test/TestIconProfile.cs | 7 ++++++- src/WebExpress.WebCore.Test/TestIconShild.cs | 7 ++++++- src/WebExpress.WebCore.Test/TestIconTool.cs | 7 ++++++- src/WebExpress.WebCore.Test/TestIconWrench.cs | 7 ++++++- src/WebExpress.WebCore/WebIcon/IIcon.cs | 7 ++++++- 7 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/WebExpress.WebCore.Test/TestIconBell.cs b/src/WebExpress.WebCore.Test/TestIconBell.cs index b42172f..2152f3e 100644 --- a/src/WebExpress.WebCore.Test/TestIconBell.cs +++ b/src/WebExpress.WebCore.Test/TestIconBell.cs @@ -14,8 +14,13 @@ public class TestIconBell : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("🔔")); } diff --git a/src/WebExpress.WebCore.Test/TestIconPalette.cs b/src/WebExpress.WebCore.Test/TestIconPalette.cs index 16ed54a..1dee1c9 100644 --- a/src/WebExpress.WebCore.Test/TestIconPalette.cs +++ b/src/WebExpress.WebCore.Test/TestIconPalette.cs @@ -14,8 +14,13 @@ public class TestIconPalette : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("🎨")); } diff --git a/src/WebExpress.WebCore.Test/TestIconProfile.cs b/src/WebExpress.WebCore.Test/TestIconProfile.cs index acf02b2..2ca070e 100644 --- a/src/WebExpress.WebCore.Test/TestIconProfile.cs +++ b/src/WebExpress.WebCore.Test/TestIconProfile.cs @@ -14,8 +14,13 @@ public class TestIconProfile : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("👤")); } diff --git a/src/WebExpress.WebCore.Test/TestIconShild.cs b/src/WebExpress.WebCore.Test/TestIconShild.cs index 73dc193..c8c786e 100644 --- a/src/WebExpress.WebCore.Test/TestIconShild.cs +++ b/src/WebExpress.WebCore.Test/TestIconShild.cs @@ -15,8 +15,13 @@ public class TestIconShild : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("🛡")); } diff --git a/src/WebExpress.WebCore.Test/TestIconTool.cs b/src/WebExpress.WebCore.Test/TestIconTool.cs index 2c36e89..86b6425 100644 --- a/src/WebExpress.WebCore.Test/TestIconTool.cs +++ b/src/WebExpress.WebCore.Test/TestIconTool.cs @@ -15,8 +15,13 @@ public class TestIconTool : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("🛠")); } diff --git a/src/WebExpress.WebCore.Test/TestIconWrench.cs b/src/WebExpress.WebCore.Test/TestIconWrench.cs index 43353fb..487b1be 100644 --- a/src/WebExpress.WebCore.Test/TestIconWrench.cs +++ b/src/WebExpress.WebCore.Test/TestIconWrench.cs @@ -15,8 +15,13 @@ public class TestIconWrench : IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree) + public IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null) { return new HtmlElementTextSemanticsSpan(new HtmlText("🔧")); } diff --git a/src/WebExpress.WebCore/WebIcon/IIcon.cs b/src/WebExpress.WebCore/WebIcon/IIcon.cs index 624cb5c..7dfd0c0 100644 --- a/src/WebExpress.WebCore/WebIcon/IIcon.cs +++ b/src/WebExpress.WebCore/WebIcon/IIcon.cs @@ -13,7 +13,12 @@ public interface IIcon /// /// The context in which the icon is rendered. /// The visual tree representing the icon's structure. + /// The id attribute of the HTML element. + /// The description of the icon. + /// The CSS class of the HTML element. + /// The inline style of the HTML element. + /// The ARIA role of the HTML element. /// An HTML node representing the rendered icon. - IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree); + IHtmlNode Render(IRenderContext renderContext, IVisualTree visualTree, string id = null, string description = null, string css = null, string style = null, string role = null); } } From 203ddee82a6904f77a1b1ec14bc18168fce8d0c1 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sun, 16 Mar 2025 14:33:21 +0100 Subject: [PATCH 140/162] refactoring --- .../Manager/UnitTestSettingPageManager.cs | 1 - src/WebExpress.WebCore.Test/TestSettingCategoryA.cs | 1 - src/WebExpress.WebCore.Test/TestSettingCategoryB.cs | 1 - src/WebExpress.WebCore.Test/TestSettingCategoryC.cs | 1 - src/WebExpress.WebCore.Test/TestSettingGroupA.cs | 1 - src/WebExpress.WebCore.Test/TestSettingGroupB.cs | 1 - src/WebExpress.WebCore.Test/TestSettingGroupC.cs | 1 - src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs | 2 +- .../WebSettingPage/ISettingCategoryContext.cs | 1 - src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs | 1 - src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs | 1 - src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs | 1 - src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs | 1 - src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs | 1 - .../WebSettingPage/{Model => }/SettingSection.cs | 2 +- 15 files changed, 2 insertions(+), 15 deletions(-) rename src/WebExpress.WebCore/WebSettingPage/{Model => }/SettingSection.cs (90%) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index 56a1bf0..bbc38a0 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -1,7 +1,6 @@ using WebExpress.WebCore.Test.Fixture; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test.Manager { diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs index 97a4499..b27f5be 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs index 2bf328f..27f9500 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs index 99f344e..88e13e8 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs +++ b/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs index 57ef17f..eabad40 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupA.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs index b349a19..320271e 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupB.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs index d62c742..4c4f0da 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs +++ b/src/WebExpress.WebCore.Test/TestSettingGroupC.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.Test { diff --git a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs index 74785dc..7fc68fb 100644 --- a/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/SettingSectionAttribute.cs @@ -1,5 +1,5 @@ using System; -using WebExpress.WebCore.WebSettingPage.Model; +using WebExpress.WebCore.WebSettingPage; namespace WebExpress.WebCore.WebAttribute { diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs index c897ae3..c0ea94c 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingCategoryContext.cs @@ -2,7 +2,6 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs index 52abfdb..56949a2 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingGroupContext.cs @@ -2,7 +2,6 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs index d685627..796801b 100644 --- a/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/ISettingPageContext.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs index 18ac99d..dee5991 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingCategoryContext.cs @@ -2,7 +2,6 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs index 918c557..591f0f5 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingGroupContext.cs @@ -2,7 +2,6 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs index ccb114c..f429492 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageContext.cs @@ -1,6 +1,5 @@ using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebPage; -using WebExpress.WebCore.WebSettingPage.Model; namespace WebExpress.WebCore.WebSettingPage { diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs b/src/WebExpress.WebCore/WebSettingPage/SettingSection.cs similarity index 90% rename from src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs rename to src/WebExpress.WebCore/WebSettingPage/SettingSection.cs index 20b6b88..a187bc4 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingSection.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingSection.cs @@ -1,4 +1,4 @@ -namespace WebExpress.WebCore.WebSettingPage.Model +namespace WebExpress.WebCore.WebSettingPage { /// /// Definition of keys for the identification of sections in Wen pages, which can be occupied by components. From a5f3b25d74f977c6a99ff4ce5307d6f2aa68ece9 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 18 Mar 2025 18:05:57 +0100 Subject: [PATCH 141/162] add: theme manager --- .../Manager/UnitTestThemeManager.cs | 158 +++++++++ src/WebExpress.WebCore.Test/TestThemeA.cs | 22 ++ src/WebExpress.WebCore.Test/TestThemeB.cs | 20 ++ .../Internationalization/de | 10 +- .../Internationalization/en | 8 +- .../WebAsset/AssetManager.cs | 92 ++--- .../WebAsset/IAssetManager.cs | 4 +- .../WebAsset/Model/AssetItemDictionary.cs | 142 +++++++- .../WebAttribute/DescriptionAttribute.cs | 2 +- .../WebAttribute/IThemeAttribute.cs | 9 + .../WebAttribute/ImageAttribute.cs | 20 ++ .../WebAttribute/NameAttribute.cs | 2 +- .../WebComponent/ComponentHub.cs | 12 +- .../WebComponent/IComponentHub.cs | 7 + .../WebEvent/EventManager.cs | 32 +- .../WebEvent/IEventManager.cs | 10 +- .../WebFragment/FragmentManager.cs | 3 +- src/WebExpress.WebCore/WebTheme/ITheme.cs | 11 + .../WebTheme/IThemeContext.cs | 43 +++ .../WebTheme/IThemeManager.cs | 52 +++ .../WebTheme/Model/ThemeItem.cs | 49 +++ .../WebTheme/Model/ThemeItemDictionary.cs | 201 +++++++++++ .../WebTheme/ThemeContext.cs | 43 +++ .../WebTheme/ThemeManager.cs | 334 ++++++++++++++++++ 24 files changed, 1183 insertions(+), 103 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs create mode 100644 src/WebExpress.WebCore.Test/TestThemeA.cs create mode 100644 src/WebExpress.WebCore.Test/TestThemeB.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/IThemeAttribute.cs create mode 100644 src/WebExpress.WebCore/WebAttribute/ImageAttribute.cs create mode 100644 src/WebExpress.WebCore/WebTheme/ITheme.cs create mode 100644 src/WebExpress.WebCore/WebTheme/IThemeContext.cs create mode 100644 src/WebExpress.WebCore/WebTheme/IThemeManager.cs create mode 100644 src/WebExpress.WebCore/WebTheme/Model/ThemeItem.cs create mode 100644 src/WebExpress.WebCore/WebTheme/Model/ThemeItemDictionary.cs create mode 100644 src/WebExpress.WebCore/WebTheme/ThemeContext.cs create mode 100644 src/WebExpress.WebCore/WebTheme/ThemeManager.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs new file mode 100644 index 0000000..08130fb --- /dev/null +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs @@ -0,0 +1,158 @@ +using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebTheme; + +namespace WebExpress.WebCore.Test.Manager +{ + /// + /// Test the theme manager. + /// + [Collection("NonParallelTests")] + public class UnitTestThemeManager + { + /// + /// Test the register function of the theme manager. + /// + [Fact] + public void Register() + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.Equal(6, componentHub.ThemeManager.Themes.Count()); + } + + /// + /// Test the remove function of the theme manager. + /// + [Fact] + public void Remove() + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var themeManager = componentHub.ThemeManager as ThemeManager; + var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); + + // test execution + themeManager.Remove(plugin); + + Assert.Empty(componentHub.ThemeManager.Themes); + } + + /// + /// Tests whether the theme manager implements interface IComponentManager. + /// + [Fact] + public void IsIComponentManager() + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + + // test execution + Assert.True(typeof(IComponentManager).IsAssignableFrom(componentHub.ThemeManager.GetType())); + } + + /// + /// Test the id property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), "webexpress.webcore.test.testthemea")] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), "webexpress.webcore.test.testthemeb")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), "webexpress.webcore.test.testthemea")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), "webexpress.webcore.test.testthemeb")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), "webexpress.webcore.test.testthemea")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), "webexpress.webcore.test.testthemeb")] + public void Id(Type applicationType, Type themeType, string id) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var themes = componentHub.ThemeManager.GetThemes(application, themeType); + + if (id == null) + { + Assert.Empty(themes); + return; + } + + Assert.Contains(id, themes.Select(x => x.ThemeId?.ToString())); + } + + /// + /// Test the name property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), "TestThemeA")] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), "TestThemeB")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), "TestThemeA")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), "TestThemeB")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), "TestThemeA")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), "TestThemeB")] + public void Name(Type applicationType, Type themeType, string name) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var themes = componentHub.ThemeManager.GetThemes(application, themeType); + + if (name == null) + { + Assert.Empty(themes); + return; + } + + Assert.Contains(name, themes?.Select(x => x.Name)); + } + + /// + /// Test the description property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), "A dummy theme for testing.")] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), "A dummy theme for testing.")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), "A dummy theme for testing.")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), null)] + public void Description(Type applicationType, Type themeType, string expected) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); + + Assert.NotNull(theme); + Assert.Equal(expected, theme?.Description); + } + + /// + /// Test the image property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), "/server/appa/webexpress.webcore.test.testthemea.png")] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), "/server/appb/webexpress.webcore.test.testthemea.png")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), "/server/webexpress.webcore.test.testthemea.png")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), null)] + public void Image(Type applicationType, Type themeType, string expected) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); + + Assert.NotNull(theme); + Assert.Equal(expected, theme?.Image); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestThemeA.cs b/src/WebExpress.WebCore.Test/TestThemeA.cs new file mode 100644 index 0000000..b46c648 --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestThemeA.cs @@ -0,0 +1,22 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebTheme; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy theme for testing. + /// + [Name("TestThemeA")] + [Description("A dummy theme for testing.")] + [Image("webexpress.webcore.test.testthemea.png")] + public sealed class TestThemeA : ITheme + { + /// + /// Returns the text color for the theme. + /// + /// + /// A string representing the text color in hexadecimal format. + /// + public static string TextColor => "000000"; + } +} diff --git a/src/WebExpress.WebCore.Test/TestThemeB.cs b/src/WebExpress.WebCore.Test/TestThemeB.cs new file mode 100644 index 0000000..a4b36ff --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestThemeB.cs @@ -0,0 +1,20 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebTheme; + +namespace WebExpress.WebCore.Test +{ + /// + /// A dummy theme for testing. + /// + [Name("TestThemeB")] + public sealed class TestThemeB : ITheme + { + /// + /// Returns the text color for the theme. + /// + /// + /// A string representing the text color in hexadecimal format. + /// + public static string TextColor => "FFFFFF"; + } +} diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index a310acf..53b04a6 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -148,7 +148,7 @@ jobmanager.initialization=Der Jobmanager wurde initialisiert. jobmanager.register=Der Job '{0}' wurde der Anwendung '{1}' zugewiesen und im Jobmanager registriert. jobmanager.duplicate=Der Job '{0}' wurde bereits in der Anwendung '{1}' registriert. jobmanager.jobless=Der Job '{0}' besitzt keine Angaben zu einem Job. -jobmanager.job=Job: '{0}' für Modul '{1}' +jobmanager.job=Job: '{0}' für Anwendung '{1}' jobmanager.job.process=Der Job '{0}' wird ausgeführt. jobmanager.cron.parseerror=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' kann nicht verarbeitet werden. jobmanager.cron.range=Syntaxfehler in der Zeitangabe eines Jobs. Der Wert '{0}' ist außerhalb des gültigen Bereiches. @@ -157,10 +157,9 @@ fragmentmanager.initialization=Der Fragmentmanager wurde initialisiert. fragmentmanager.register=Das Fragment '{0}' wurde in der Sektion '{1}' der Anwendung '{2}' registriert. fragmentmanager.error.section=Die Angabe der Sektion ist fehlerhaft. fragmentmanager.wrongtype=Der Typ '{0}' implementiert die Schnittstelle '{1}' nicht. -fragmentmanager.moduleless=Das Fragment '{0}' aus dem Plugin '{1}' besitzt keine Angaben zum Modul. fragmentmanager.addfragment.duplicate=Das Fragment '{0}' aus dem Plugin '{1}' ist bereits hinzugefügt worden. fragmentmanager.titel=Fragmente: -fragmentmanager.fragment=Fragment: '{0}' +fragmentmanager.fragment=Fragment: '{0}' für die Anwendung '{1}'. identitymanager.initialization=Der Identitymanager wurde initialisiert. identitymanager.registerpermission=Die Berechtigung '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. @@ -168,5 +167,10 @@ identitymanager.duplicatepermission=Die Berechtigung '{0}' wurde bereits in der identitymanager.registerrole=Die Rolle '{0}' wurde der Anwendung '{1}' zugewiesen und im Identitymanager registriert. identitymanager.duplicaterole=Die Rolle '{0}' wurde bereits in der Anwendung '{1}' registriert. +thememanager.initialization=Der Thememanager wurde initialisiert. +thememanager.addtheme=Das Theme '{0}' wurde in der Anwendung '{1}' registiert. +thememanager.titel=Designvorlagen: +thememanager.theme=Designvorlage: '{0}' für die Anwendung '{1}'. + resource.variable.duplicate=Variable '{0}' bereits vorhanden! resource.file={0}: Datei '{1}' wurde geladen. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 3b28a81..8564eaf 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -157,10 +157,9 @@ fragmentmanager.initialization=The fragment manager has been initialized. fragmentmanager.register=The fragment '{0}' has been registered in the '{1}' section of application '{2}'. fragmentmanager.error.section=The section is incorrect. fragmentmanager.wrongtype=The type '{0}' does not implement the interface '{1}'. -fragmentmanager.moduleless=The fragment '{0}' from the plugin '{1}' has no information about the module. fragmentmanager.addfragment.duplicate=The fragment '{0}' from the plugin '{1}' has already been added. fragmentmanager.titel=Fragments: -fragmentmanager.fragment=Fragment: '{0}' +fragmentmanager.fragment=Fragment: '{0}' for application '{1}'. identitymanager.initialization=The identity manager has been initialized. identitymanager.registerpermission=The permission '{0}' has been assigned to the application '{1}' and registered in the identity manager. @@ -168,5 +167,10 @@ identitymanager.duplicatepermission=The permission '{0}' has already been regist identitymanager.registerrole=The role '{0}' has been assigned to the application '{1}' and registered in the identity manager. identitymanager.duplicaterole=The role '{0}' has already been registered in the application '{1}'. +thememanager.initialization=The theme manager has been initialized. +thememanager.addtheme=The theme '{0}' has been registered in the application '{1}'. +thememanager.titel=Themes: +thememanager.theme=Theme: '{0}' for application '{1}'. + resource.variable.duplicate=Variable '{0}' already exists! resource.file={0}: File '{1}' has been loaded. diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index 8c1c5c7..bb38b28 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -9,7 +9,6 @@ using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebResource; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebAsset @@ -21,26 +20,23 @@ public sealed class AssetManager : IAssetManager, ISystemComponent { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; - private readonly AssetItemDictionary _itemDictionary = []; + private readonly AssetItemDictionary _itemDictionary = new(); private readonly AssetEndpointDictionary _endpointDictionary = []; /// /// An event that fires when an asset is added. /// - public event EventHandler AddResource; + public event EventHandler AddAsset; /// /// An event that fires when an asset is removed. /// - public event EventHandler RemoveResource; + public event EventHandler RemoveAsset; /// /// Returns all asset contexts. /// - public IEnumerable Assets => _itemDictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x) - .Select(x => x.AssetContext); + public IEnumerable Assets => _itemDictionary.All.Select(x => x.AssetContext); /// /// Initializes a new instance of the class. @@ -70,9 +66,7 @@ private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerCo HandleRequest = (request, endpointContext) => { var assetContext = endpointContext as IAssetContext; - var asset = _itemDictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x) + var asset = _itemDictionary.All .FirstOrDefault(x => request.Uri.ToString().ToLower().Replace('/', '.').EndsWith(x.AssetContext.EndpointId.ToString())); if (asset != null) @@ -84,8 +78,8 @@ private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerCo } }; - AddResource += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); - RemoveResource += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); + AddAsset += (sender, e) => endpointtRegistration.AddEndpoint?.Invoke(sender, e); + RemoveAsset += (sender, e) => endpointtRegistration.RemoveEndpoint?.Invoke(sender, e); _componentHub.EndpointManager.Register(endpointtRegistration); @@ -103,7 +97,7 @@ private AssetManager(IComponentHub componentHub, IHttpServerContext httpServerCo /// The context of the plugin whose resources are to be associated. private void Register(IPluginContext pluginContext) { - if (_itemDictionary.ContainsKey(pluginContext)) + if (_itemDictionary.ContainsPlugin(pluginContext)) { return; } @@ -119,7 +113,7 @@ private void Register(IApplicationContext applicationContext) { foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) { - if (_itemDictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + if (_itemDictionary.ContainsApplication(pluginContext, applicationContext)) { continue; } @@ -165,7 +159,7 @@ private void Register(IPluginContext pluginContext, IEnumerableThe context of the plugin that contains the resources to remove. internal void Remove(IPluginContext pluginContext) { - if (pluginContext == null) + foreach (var assetContext in _itemDictionary.Remove(pluginContext)) { - return; - } - - // the plugin has not been registered in the manager - if (_itemDictionary.TryGetValue(pluginContext, out var value)) - { - foreach (var resourceItem in value.Values - .SelectMany(x => x)) - { - OnRemoveResource(resourceItem.AssetContext); - resourceItem.Dispose(); - } - - _itemDictionary.Remove(pluginContext); + OnRemoveAsset(assetContext); } } @@ -210,23 +191,9 @@ internal void Remove(IPluginContext pluginContext) /// The context of the application that contains the resources to remove. internal void Remove(IApplicationContext applicationContext) { - if (applicationContext == null) + foreach (var assetContext in _itemDictionary.Remove(applicationContext)) { - return; - } - - foreach (var pluginDict in _itemDictionary.Values) - { - foreach (var assetList in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) - { - foreach (var assetItem in assetList) - { - OnRemoveResource(assetItem.AssetContext); - assetItem.Dispose(); - } - } - - pluginDict.Remove(applicationContext); + OnRemoveAsset(assetContext); } } @@ -237,14 +204,7 @@ internal void Remove(IApplicationContext applicationContext) /// An enumeration of asset contexts. public IEnumerable GetAssets(IPluginContext pluginContext) { - if (_itemDictionary.TryGetValue(pluginContext, out var pluginResources)) - { - return pluginResources - .SelectMany(x => x.Value) - .Select(x => x.AssetContext); - } - - return []; + return _itemDictionary.GetAssets(pluginContext); } /// @@ -254,29 +214,25 @@ public IEnumerable GetAssets(IPluginContext pluginContext) /// An enumeration of asset contextes. public IEnumerable GetAssets(IApplicationContext applicationContext) { - return _itemDictionary.Values - .SelectMany(x => x.Values) - .SelectMany(x => x) - .Where(x => x.AssetContext.ApplicationContext.Equals(applicationContext)) - .Select(x => x.AssetContext); + return _itemDictionary.GetAssets(applicationContext); } /// - /// Raises the AddResource event. + /// Raises the AddAsset event. /// - /// The asset context. - private void OnAddResource(IAssetContext resourceContext) + /// The asset context. + private void OnAddAsset(IAssetContext assetContext) { - AddResource?.Invoke(this, resourceContext); + AddAsset?.Invoke(this, assetContext); } /// - /// Raises the RemoveResource event. + /// Raises the RemoveAsset event. /// - /// The asset context. - private void OnRemoveResource(IAssetContext resourceContext) + /// The asset context. + private void OnRemoveAsset(IAssetContext assetContext) { - RemoveResource?.Invoke(this, resourceContext); + RemoveAsset?.Invoke(this, assetContext); } /// diff --git a/src/WebExpress.WebCore/WebAsset/IAssetManager.cs b/src/WebExpress.WebCore/WebAsset/IAssetManager.cs index 3848b20..d97cca3 100644 --- a/src/WebExpress.WebCore/WebAsset/IAssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/IAssetManager.cs @@ -14,12 +14,12 @@ public interface IAssetManager : IComponentManager /// /// An event that fires when an asset is added. /// - event EventHandler AddResource; + event EventHandler AddAsset; /// /// An event that fires when an asset is removed. /// - event EventHandler RemoveResource; + event EventHandler RemoveAsset; /// /// Returns all asset contexts. diff --git a/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs b/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs index fa13d34..4924daa 100644 --- a/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs +++ b/src/WebExpress.WebCore/WebAsset/Model/AssetItemDictionary.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebPlugin; @@ -9,8 +10,17 @@ namespace WebExpress.WebCore.WebAsset.Model /// key = plugin context /// value = { key = resource type, value = ressource item } /// - internal class AssetItemDictionary : Dictionary>> + internal class AssetItemDictionary { + private readonly Dictionary>> _dictionary = []; + + /// + /// Returns all asset items. + /// + public IEnumerable All => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x); + /// /// Adds a asset item to the dictionary. /// @@ -27,12 +37,12 @@ public bool AddAssetItem(IPluginContext pluginContext, IApplicationContext appli return false; } - if (!ContainsKey(pluginContext)) + if (!_dictionary.ContainsKey(pluginContext)) { - this[pluginContext] = []; + _dictionary[pluginContext] = []; } - var appContextDict = this[pluginContext]; + var appContextDict = _dictionary[pluginContext]; if (!appContextDict.TryGetValue(applicationContext, out List value)) { @@ -48,6 +58,95 @@ public bool AddAssetItem(IPluginContext pluginContext, IApplicationContext appli return true; } + /// + /// Removes all resources associated with the specified plugin context. + /// + /// The context of the plugin that contains the resources to remove. + /// An enumeration of asset contexts that were removed. + public IEnumerable Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return []; + } + + // the plugin has not been registered in the manager + if (_dictionary.TryGetValue(pluginContext, out var value)) + { + var items = value.Values + .SelectMany(x => x) + .ToList(); + + foreach (var assetItem in items) + { + assetItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + + return items.Select(x => x.AssetContext); + } + + return []; + } + + /// + /// Removes all assets associated with the specified application context. + /// + /// The context of the application that contains the resources to remove. + /// An enumeration of asset contexts that were removed. + internal IEnumerable Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return []; + } + + var removedAssets = new List(); + + foreach (var pluginDict in _dictionary.Values) + { + foreach (var assetList in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var assetItem in assetList) + { + removedAssets.Add(assetItem.AssetContext); + assetItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + + return removedAssets; + } + + /// + /// Checks if the dictionary contains the specified plugin context. + /// + /// The plugin context to check. + /// True if the plugin context exists in the dictionary, otherwise false. + public bool ContainsPlugin(IPluginContext pluginContext) + { + return _dictionary.ContainsKey(pluginContext); + } + + /// + /// Checks if the dictionary contains the specified application context. + /// + /// The plugin context to check. + /// The application context to check. + /// True if the application context exists in the dictionary, otherwise false. + public bool ContainsApplication(IPluginContext pluginContext, IApplicationContext applicationContext) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + return true; + } + + return false; + } + /// /// Returns the asset items from the dictionary. /// @@ -55,9 +154,9 @@ public bool AddAssetItem(IPluginContext pluginContext, IApplicationContext appli /// An IEnumerable of asset items public IEnumerable GetAssetItems(IApplicationContext applicationContext) { - if (ContainsKey(applicationContext?.PluginContext)) + if (_dictionary.ContainsKey(applicationContext?.PluginContext)) { - var appContextDict = this[applicationContext?.PluginContext]; + var appContextDict = _dictionary[applicationContext?.PluginContext]; if (appContextDict.TryGetValue(applicationContext, out List value)) { @@ -69,5 +168,36 @@ public IEnumerable GetAssetItems(IApplicationContext applicationConte return []; } + + /// + /// Returns an enumeration of all containing asset contexts of a plugin. + /// + /// A context of a plugin whose asset are to be registered. + /// An enumeration of asset contexts. + public IEnumerable GetAssets(IPluginContext pluginContext) + { + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) + { + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.AssetContext); + } + + return []; + } + + /// + /// Returns an enumeration of asset contextes. + /// + /// The context of the application. + /// An enumeration of asset contextes. + public IEnumerable GetAssets(IApplicationContext applicationContext) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Where(x => x.AssetContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.AssetContext); + } } } diff --git a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs index ebc7138..750f2a4 100644 --- a/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/DescriptionAttribute.cs @@ -7,7 +7,7 @@ namespace WebExpress.WebCore.WebAttribute /// Implements , , and . /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class DescriptionAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute + public class DescriptionAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute, IThemeAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebAttribute/IThemeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/IThemeAttribute.cs new file mode 100644 index 0000000..f34e542 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/IThemeAttribute.cs @@ -0,0 +1,9 @@ +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Interface of a theme assignment attribute. + /// + public interface IThemeAttribute + { + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/ImageAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ImageAttribute.cs new file mode 100644 index 0000000..0ed9d7a --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ImageAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify an image for a theme. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ImageAttribute : Attribute, IThemeAttribute + { + /// + /// Initializes a new instance of the class. + /// + /// The image. + public ImageAttribute(string image) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs index 0a978b6..e9d30b4 100644 --- a/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs +++ b/src/WebExpress.WebCore/WebAttribute/NameAttribute.cs @@ -6,7 +6,7 @@ namespace WebExpress.WebCore.WebAttribute /// Attribute to assign a name to a class. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class NameAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute + public class NameAttribute : Attribute, IPluginAttribute, IApplicationAttribute, ISettingCategoryAttribute, ISettingGroupAttribute, IThemeAttribute { /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs index 2099db9..cbbea77 100644 --- a/src/WebExpress.WebCore/WebComponent/ComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/ComponentHub.cs @@ -21,6 +21,7 @@ using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebTask; +using WebExpress.WebCore.WebTheme; namespace WebExpress.WebCore.WebComponent { @@ -50,6 +51,7 @@ public class ComponentHub : IComponentHub private readonly JobManager _jobManager; private readonly TaskManager _taskManager; private readonly IdentityManager _identityManager; + private readonly ThemeManager _themeManager; private int _lastCounter = 0; /// @@ -85,7 +87,8 @@ public class ComponentHub : IComponentHub _internationalizationManager, _identityManager, _sessionManager, - _taskManager + _taskManager, + _themeManager }.Concat(_dictionary.Values.SelectMany(x => x).Select(x => x.ComponentInstance)); /// @@ -202,6 +205,12 @@ public class ComponentHub : IComponentHub /// The instance of the session manager. public ISessionManager SessionManager => _sessionManager; + /// + /// Returns the theme manager. + /// + /// The instance of the theme manager. + public IThemeManager ThemeManager => _themeManager; + /// /// Initializes a new instance of the class. /// @@ -231,6 +240,7 @@ protected ComponentHub(IHttpServerContext httpServerContext) _sessionManager = CreateInstance(typeof(SessionManager)) as SessionManager; _taskManager = CreateInstance(typeof(TaskManager)) as TaskManager; _identityManager = CreateInstance(typeof(IdentityManager)) as IdentityManager; + _themeManager = CreateInstance(typeof(ThemeManager)) as ThemeManager; _internationalizationManager.Register(typeof(HttpServer).Assembly, typeof(HttpServer).Assembly.GetName().Name?.ToLower()); diff --git a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs index a0ac758..13ac642 100644 --- a/src/WebExpress.WebCore/WebComponent/IComponentHub.cs +++ b/src/WebExpress.WebCore/WebComponent/IComponentHub.cs @@ -19,6 +19,7 @@ using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebStatusPage; using WebExpress.WebCore.WebTask; +using WebExpress.WebCore.WebTheme; namespace WebExpress.WebCore.WebComponent { @@ -156,6 +157,12 @@ public interface IComponentHub : IComponentManager /// The instance of the session manager. ISessionManager SessionManager { get; } + /// + /// Returns the theme manager. + /// + /// The instance of the theme manager. + IThemeManager ThemeManager { get; } + /// /// Returns a component based on its id. /// diff --git a/src/WebExpress.WebCore/WebEvent/EventManager.cs b/src/WebExpress.WebCore/WebEvent/EventManager.cs index b0a6d95..04d6b87 100644 --- a/src/WebExpress.WebCore/WebEvent/EventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/EventManager.cs @@ -69,12 +69,13 @@ private EventManager(IComponentHub componentHub, IHttpServerContext httpServerCo /// /// Returns the event handler contexts. /// - /// The type of event. + /// The type of event. /// The application context. /// An IEnumerable of event handler contexts. - public IEnumerable GetEventHandlers(IApplicationContext applicationContext) where T : IEvent + public IEnumerable GetEventHandlers(IApplicationContext applicationContext) + where TEvent : IEvent { - return _dictionary.GetEventHandlerItems(applicationContext) + return _dictionary.GetEventHandlerItems(applicationContext) .Select(x => x.EventHandlerContext); } @@ -93,13 +94,14 @@ public IEnumerable GetEventHandlers(IApplicationContext ap /// /// Raises the specified event. /// - /// The type of event. + /// The type of event. /// The application context. /// The sender object. /// The event argument. - public void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) where T : IEvent + public void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) + where TEvent : IEvent { - var eventHandlers = _dictionary.GetEventHandlerItems(applicationContext); + var eventHandlers = _dictionary.GetEventHandlerItems(applicationContext); foreach (var eventHandler in eventHandlers) { @@ -317,23 +319,23 @@ private void OnRemovePlugin(object sender, IPluginContext e) } /// - /// Raises the event when an application is removed. + /// Raises the event when an application is added. /// /// The source of the event. - /// The context of the application being removed. - private void OnRemoveApplication(object sender, IApplicationContext e) + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) { - Remove(e); + Register(e); } /// - /// Raises the event when an application is added. + /// Raises the event when an application is removed. /// /// The source of the event. - /// The context of the application being added. - private void OnAddApplication(object sender, IApplicationContext e) + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) { - Register(e); + Remove(e); } /// @@ -372,6 +374,8 @@ public void Dispose() _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; _componentHub.ApplicationManager.AddApplication -= OnAddApplication; _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); } } } diff --git a/src/WebExpress.WebCore/WebEvent/IEventManager.cs b/src/WebExpress.WebCore/WebEvent/IEventManager.cs index 70d4396..d725df2 100644 --- a/src/WebExpress.WebCore/WebEvent/IEventManager.cs +++ b/src/WebExpress.WebCore/WebEvent/IEventManager.cs @@ -18,10 +18,11 @@ public interface IEventManager : IComponentManager /// /// Returns the event handler contexts. /// - /// The type of event. + /// The type of event. /// The application context. /// An IEnumerable of event handler contexts. - IEnumerable GetEventHandlers(IApplicationContext applicationContext) where T : IEvent; + IEnumerable GetEventHandlers(IApplicationContext applicationContext) + where TEvent : IEvent; /// /// Returns the event handler contexts. @@ -34,10 +35,11 @@ public interface IEventManager : IComponentManager /// /// Raises the specified event. /// - /// The type of event. + /// The type of event. /// The application context. /// The sender object. /// The event argument. - void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) where T : IEvent; + void RaiseEvent(IApplicationContext applicationContext, object sender, IEventArgument argument) + where TEvent : IEvent; } } diff --git a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs index ff75707..e509279 100644 --- a/src/WebExpress.WebCore/WebFragment/FragmentManager.cs +++ b/src/WebExpress.WebCore/WebFragment/FragmentManager.cs @@ -469,12 +469,13 @@ private void Log() { I18N.Translate("webexpress.webcore:fragmentmanager.titel") }; + foreach (var fragment in Fragments.Distinct()) { list.Add ( string.Empty.PadRight(2) + - I18N.Translate("webexpress.webcore:fragmentmanager.fragment", fragment.FragmentId.ToString()) + I18N.Translate("webexpress.webcore:fragmentmanager.fragment", fragment.FragmentId.ToString(), fragment.ApplicationContext?.ApplicationId) ); } diff --git a/src/WebExpress.WebCore/WebTheme/ITheme.cs b/src/WebExpress.WebCore/WebTheme/ITheme.cs new file mode 100644 index 0000000..f6e9118 --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/ITheme.cs @@ -0,0 +1,11 @@ +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebTheme +{ + /// + /// Represents a theme component in the web application. + /// + public interface ITheme : IComponent + { + } +} diff --git a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs new file mode 100644 index 0000000..103fc68 --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs @@ -0,0 +1,43 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebTheme +{ + /// + /// Represents the context for a theme in the web application. + /// + public interface IThemeContext : IContext + { + /// + /// Returns the theme id. + /// + IComponentId ThemeId { get; } + + /// + /// Returns the associated plugin context. + /// + IPluginContext PluginContext { get; } + + /// + /// Returns the corresponding application context. + /// + IApplicationContext ApplicationContext { get; } + + /// + /// Returns the image associated with the theme. + /// + UriResource Image { get; } + + /// + /// Returns the name of the theme. + /// + string Name { get; } + + /// + /// Returns the description of the theme. + /// + string Description { get; } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/IThemeManager.cs b/src/WebExpress.WebCore/WebTheme/IThemeManager.cs new file mode 100644 index 0000000..21ee3ae --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/IThemeManager.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; + +namespace WebExpress.WebCore.WebTheme +{ + /// + /// Interface for managing themes within the web application. + /// + public interface IThemeManager : IComponentManager + { + /// + /// An event that fires when a theme is added. + /// + event EventHandler AddTheme; + + /// + /// An event that fires when a theme is removed. + /// + event EventHandler RemoveTheme; + + /// + /// Returns the collection of themes. + /// + IEnumerable Themes { get; } + + /// + /// Returns the theme contexts. + /// + /// The type of theme. + /// The application context. + /// An IEnumerable of theme contexts. + IEnumerable GetThemes(IApplicationContext applicationContext) + where TTheme : ITheme; + + /// + /// Returns the theme contexts. + /// + /// The application context. + /// The type of theme. + /// An IEnumerable of theme contexts. + IEnumerable GetThemes(IApplicationContext applicationContext, Type themeType); + + /// + /// Returns the theme associated with the specified theme context. + /// + /// The context of the theme to retrieve. + /// The theme associated with the specified context. + ITheme GetTheme(IThemeContext themeContext); + } +} diff --git a/src/WebExpress.WebCore/WebTheme/Model/ThemeItem.cs b/src/WebExpress.WebCore/WebTheme/Model/ThemeItem.cs new file mode 100644 index 0000000..a21cd80 --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/Model/ThemeItem.cs @@ -0,0 +1,49 @@ +using System; + +namespace WebExpress.WebCore.WebTheme.Model +{ + /// + /// Represents an theme item. + /// + public class ThemeItem : IDisposable + { + /// + /// Returns or sets the type of theme. + /// + public Type ThemeClass { get; set; } + + /// + /// Returns or sets the instance of the theme. + /// + public ITheme Instance { get; set; } + + /// + /// Returns the theme context. + /// + public IThemeContext ThemeContext { get; internal set; } + + /// + /// Initializes a new instance of the class. + /// + internal ThemeItem() + { + } + + /// + /// Performs application-specific tasks related to sharing, returning, or resetting unmanaged resources. + /// + public void Dispose() + { + + } + + /// + /// Convert the theme element to a string. + /// + /// The theme element in its string representation. + public override string ToString() + { + return $"Theme: '{ThemeContext?.ThemeId}'"; + } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/Model/ThemeItemDictionary.cs b/src/WebExpress.WebCore/WebTheme/Model/ThemeItemDictionary.cs new file mode 100644 index 0000000..bfc38df --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/Model/ThemeItemDictionary.cs @@ -0,0 +1,201 @@ +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebPlugin; + +namespace WebExpress.WebCore.WebTheme.Model +{ + /// + /// Represents a dictionary that stores theme items based on plugin and application contexts. + /// + internal class ThemeItemDictionary + { + private readonly Dictionary>> _dictionary = new(); + + /// + /// Returns all theme items. + /// + public IEnumerable All => _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x); + + /// + /// Adds a theme item to the dictionary. + /// + /// The plugin context. + /// The application context. + /// The theme item. + /// True if the theme item was added successfully, false if an element with the same status code already exists. + public bool AddThemeItem(IPluginContext pluginContext, IApplicationContext applicationContext, ThemeItem themeItem) + { + var type = themeItem.ThemeClass; + + if (!typeof(ITheme).IsAssignableFrom(type)) + { + return false; + } + + if (!_dictionary.ContainsKey(pluginContext)) + { + _dictionary[pluginContext] = []; + } + + var appContextDict = _dictionary[pluginContext]; + + if (!appContextDict.TryGetValue(applicationContext, out List value)) + { + value = []; + appContextDict[applicationContext] = value; + } + + var assetList = value; + + assetList.RemoveAll(x => x.ThemeContext?.ThemeId == themeItem.ThemeContext.ThemeId); + assetList.Add(themeItem); + + return true; + } + + /// + /// Removes all resources associated with the specified plugin context. + /// + /// The context of the plugin that contains the resources to remove. + /// An enumeration of theme contexts that were removed. + public IEnumerable Remove(IPluginContext pluginContext) + { + if (pluginContext == null) + { + return []; + } + + // the plugin has not been registered in the manager + if (_dictionary.TryGetValue(pluginContext, out var value)) + { + var items = value.Values + .SelectMany(x => x) + .ToList(); + + foreach (var assetItem in items) + { + assetItem.Dispose(); + } + + _dictionary.Remove(pluginContext); + + return items.Select(x => x.ThemeContext); + } + + return []; + } + + /// + /// Removes all themes associated with the specified application context. + /// + /// The context of the application that contains the resources to remove. + /// An enumeration of theme contexts that were removed. + internal IEnumerable Remove(IApplicationContext applicationContext) + { + if (applicationContext == null) + { + return []; + } + + var removedThemes = new List(); + + foreach (var pluginDict in _dictionary.Values) + { + foreach (var themeList in pluginDict.Where(x => x.Key == applicationContext).Select(x => x.Value)) + { + foreach (var assetItem in themeList) + { + removedThemes.Add(assetItem.ThemeContext); + assetItem.Dispose(); + } + } + + pluginDict.Remove(applicationContext); + } + + return removedThemes; + } + + /// + /// Checks if the dictionary contains the specified plugin context. + /// + /// The plugin context to check. + /// True if the plugin context exists in the dictionary, otherwise false. + public bool ContainsPlugin(IPluginContext pluginContext) + { + return _dictionary.ContainsKey(pluginContext); + } + + /// + /// Checks if the dictionary contains the specified application context. + /// + /// The plugin context to check. + /// The application context to check. + /// True if the application context exists in the dictionary, otherwise false. + public bool ContainsApplication(IPluginContext pluginContext, IApplicationContext applicationContext) + { + if (_dictionary.TryGetValue(pluginContext, out var appDict) && appDict.ContainsKey(applicationContext)) + { + return true; + } + + return false; + } + + /// + /// Returns the theme items from the dictionary. + /// + /// The application context. + /// An IEnumerable of theme items + public IEnumerable GetThemeItems(IApplicationContext applicationContext) + { + if (_dictionary.ContainsKey(applicationContext?.PluginContext)) + { + var appContextDict = _dictionary[applicationContext?.PluginContext]; + + if (appContextDict.TryGetValue(applicationContext, out List value)) + { + var assetList = value; + + return assetList; + } + } + + return []; + } + + /// + /// Returns an enumeration of all containing theme contexts of a plugin. + /// + /// A context of a plugin whose theme are to be registered. + /// An enumeration of theme contexts. + public IEnumerable GetThemes(IPluginContext pluginContext) + { + if (_dictionary.TryGetValue(pluginContext, out var pluginResources)) + { + return pluginResources + .SelectMany(x => x.Value) + .Select(x => x.ThemeContext); + } + + return []; + } + + /// + /// Returns an enumeration of theme contextes. + /// + /// The context of the application. + /// An enumeration of theme contextes. + public IEnumerable GetThemes(IApplicationContext applicationContext) + { + return _dictionary.Values + .SelectMany(x => x.Values) + .SelectMany(x => x) + .Where(x => x.ThemeContext.ApplicationContext.Equals(applicationContext)) + .Select(x => x.ThemeContext); + } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs new file mode 100644 index 0000000..8580f33 --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs @@ -0,0 +1,43 @@ +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebTheme +{ + /// + /// Represents the context for a theme in the web application. + /// + public class ThemeContext : IThemeContext + { + /// + /// Returns the theme id. + /// + public IComponentId ThemeId { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } + + /// + /// Returns the corresponding application context. + /// + public IApplicationContext ApplicationContext { get; internal set; } + + /// + /// Returns the image associated with the theme. + /// + public UriResource Image { get; internal set; } + + /// + /// Returns the name of the theme. + /// + public string Name { get; internal set; } + + /// + /// Returns the description of the theme. + /// + public string Description { get; internal set; } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs new file mode 100644 index 0000000..7742069 --- /dev/null +++ b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using WebExpress.WebCore.Internationalization; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebLog; +using WebExpress.WebCore.WebPlugin; +using WebExpress.WebCore.WebTheme.Model; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebTheme +{ + /// + /// Manages themes for the web application. + /// + public class ThemeManager : IThemeManager + { + private readonly IComponentHub _componentHub; + private readonly IHttpServerContext _httpServerContext; + private readonly ThemeItemDictionary _itemDictionary = new(); + + /// + /// Event triggered when a theme is added. + /// + public event EventHandler AddTheme; + + /// + /// Event triggered when a theme is removed. + /// + public event EventHandler RemoveTheme; + + /// + /// Returns the collection of themes. + /// + public IEnumerable Themes => _itemDictionary.All.Select(x => x.ThemeContext); + + /// + /// Initializes a new instance of the class. + /// + /// The component hub. + /// The reference to the context of the host. + [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Used via Reflection.")] + private ThemeManager(IComponentHub componentHub, IHttpServerContext httpServerContext) + { + _componentHub = componentHub; + + _componentHub.PluginManager.AddPlugin += OnAddPlugin; + _componentHub.PluginManager.RemovePlugin += OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication += OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication += OnRemoveApplication; + + _httpServerContext = httpServerContext; + + _httpServerContext.Log.Debug + ( + I18N.Translate("webexpress.webcore:thememanager.initialization") + ); + } + + /// + /// Returns the theme contexts. + /// + /// The type of theme. + /// The application context. + /// An IEnumerable of theme contexts. + public IEnumerable GetThemes(IApplicationContext applicationContext) + where TTheme : ITheme + { + return GetThemes(applicationContext, typeof(TTheme)); + } + + /// + /// Returns the theme contexts. + /// + /// The application context. + /// The type of theme. + /// An IEnumerable of theme contexts. + public IEnumerable GetThemes(IApplicationContext applicationContext, Type themeType) + { + return _itemDictionary.GetThemeItems(applicationContext) + .Where(x => x.ThemeClass == themeType) + .Select(x => x.ThemeContext); + } + + /// + /// Returns the theme associated with the specified theme context. + /// + /// The context of the theme to retrieve. + /// The theme associated with the specified context. + public ITheme GetTheme(IThemeContext themeContext) + { + return _itemDictionary.GetThemeItems(themeContext.ApplicationContext) + .Where(x => x.ThemeContext == themeContext) + .Select(x => x.Instance) + .FirstOrDefault(); + } + + /// + /// Discovers and binds resources to an application. + /// + /// The context of the plugin whose resources are to be associated. + private void Register(IPluginContext pluginContext) + { + if (_itemDictionary.ContainsPlugin(pluginContext)) + { + return; + } + + Register(pluginContext, _componentHub.ApplicationManager.GetApplications(pluginContext)); + } + + /// + /// Discovers and binds resources to an application. + /// + /// The context of the application whose resources are to be associated. + private void Register(IApplicationContext applicationContext) + { + foreach (var pluginContext in _componentHub.PluginManager.GetPlugins(applicationContext)) + { + if (_itemDictionary.ContainsApplication(pluginContext, applicationContext)) + { + continue; + } + + Register(pluginContext, [applicationContext]); + } + } + + /// + /// Registers resources for a given plugin and application context. + /// + /// The plugin context. + /// The application context (optional). + private void Register(IPluginContext pluginContext, IEnumerable applicationContexts) + { + var assembly = pluginContext?.Assembly; + var assemblName = assembly.GetName().Name; + var themeTypes = assembly.GetTypes().Where + ( + x => x.IsClass == true && + x.IsSealed && + x.IsPublic && + ( + x.GetInterface(typeof(ITheme).Name) != null + ) + ); + + foreach (var themeType in themeTypes) + { + var id = themeType.FullName?.ToLower(); + var image = default(string); + var name = default(string); + var description = default(string); + + foreach (var customAttribute in themeType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IThemeAttribute)))) + { + if (customAttribute.AttributeType == typeof(ImageAttribute)) + { + image ??= customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(NameAttribute)) + { + name ??= customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + else if (customAttribute.AttributeType == typeof(DescriptionAttribute)) + { + description ??= customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); + } + } + + // assign the theme to existing applications + foreach (var applicationContext in applicationContexts) + { + var themeContext = new ThemeContext() + { + ThemeId = new ComponentId(id), + PluginContext = pluginContext, + ApplicationContext = applicationContext, + Name = name, + Description = description, + Image = image != null ? UriResource.Combine(applicationContext.ContextPath, image) : null + }; + + var themeItem = new ThemeItem() + { + ThemeClass = themeType, + ThemeContext = themeContext, + Instance = ComponentActivator.CreateInstance(themeType, themeContext, _httpServerContext, _componentHub, themeType), + }; + + if (_itemDictionary.AddThemeItem(pluginContext, applicationContext, themeItem)) + { + OnAddTheme(themeContext); + _httpServerContext?.Log.Debug( + I18N.Translate( + "webexpress.webcore:thememanager.addtheme", + id, + applicationContext.ApplicationId + ) + ); + } + } + } + + Log(); + } + + /// + /// Removes all resources associated with the specified plugin context. + /// + /// The context of the plugin that contains the resources to remove. + internal void Remove(IPluginContext pluginContext) + { + foreach (var themeContext in _itemDictionary.Remove(pluginContext)) + { + OnRemoveTheme(themeContext); + } + } + + /// + /// Removes all assets associated with the specified application context. + /// + /// The context of the application that contains the resources to remove. + internal void Remove(IApplicationContext applicationContext) + { + foreach (var assetContext in _itemDictionary.Remove(applicationContext)) + { + OnRemoveTheme(assetContext); + } + } + + /// + /// Raises the AddTheme event. + /// + /// The theme context. + private void OnAddTheme(IThemeContext themeContext) + { + AddTheme?.Invoke(this, themeContext); + } + + /// + /// Raises the RemoveTheme event. + /// + /// The theme context. + private void OnRemoveTheme(IThemeContext themeContext) + { + RemoveTheme?.Invoke(this, themeContext); + } + + /// + /// Raises the event when an plugin is added. + /// + /// The source of the event. + /// The context of the plugin being added. + private void OnAddPlugin(object sender, IPluginContext e) + { + Register(e); + } + + /// + /// Raises the event when a plugin is removed. + /// + /// The source of the event. + /// The context of the plugin being removed. + private void OnRemovePlugin(object sender, IPluginContext e) + { + Remove(e); + } + + /// + /// Raises the event when an application is added. + /// + /// The source of the event. + /// The context of the application being added. + private void OnAddApplication(object sender, IApplicationContext e) + { + Register(e); + } + + /// + /// Raises the event when an application is removed. + /// + /// The source of the event. + /// The context of the application being removed. + private void OnRemoveApplication(object sender, IApplicationContext e) + { + Remove(e); + } + + /// + /// Information about the component is collected and prepared for output in the log. + /// + private void Log() + { + if (!Themes.Any()) + { + return; + } + + using var frame = new LogFrameSimple(_httpServerContext.Log); + var list = new List + { + I18N.Translate("webexpress.webcore:thememanager.titel") + }; + + foreach (var eventHandlerContext in Themes) + { + list.Add + ( + I18N.Translate("webexpress.webcore:thememanager.theme", eventHandlerContext.ThemeId, eventHandlerContext.ApplicationContext?.ApplicationId) + ); + } + + _httpServerContext.Log.Info(string.Join(Environment.NewLine, list)); + } + + /// + /// Releases all resources used by the ThemeManager. + /// + public void Dispose() + { + _componentHub.PluginManager.AddPlugin -= OnAddPlugin; + _componentHub.PluginManager.RemovePlugin -= OnRemovePlugin; + _componentHub.ApplicationManager.AddApplication -= OnAddApplication; + _componentHub.ApplicationManager.RemoveApplication -= OnRemoveApplication; + + GC.SuppressFinalize(this); + } + } +} From 2e0a29a2fc85c130609b28756e54dab6980ccd74 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Wed, 19 Mar 2025 18:18:43 +0100 Subject: [PATCH 142/162] add: theme mode - dark and light --- .../Manager/UnitTestThemeManager.cs | 23 +++++++++++++++++++ src/WebExpress.WebCore.Test/TestThemeA.cs | 3 ++- src/WebExpress.WebCore.Test/TestThemeB.cs | 2 +- .../WebAttribute/ThemeModeAttribute.cs | 21 +++++++++++++++++ .../WebTheme/IThemeContext.cs | 5 ++++ .../WebTheme/ThemeContext.cs | 5 ++++ .../WebTheme/ThemeManager.cs | 15 +++++++++++- src/WebExpress.WebCore/WebTheme/ThemeMode.cs | 18 +++++++++++++++ 8 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/ThemeModeAttribute.cs create mode 100644 src/WebExpress.WebCore/WebTheme/ThemeMode.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs index 08130fb..dd44f1c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs @@ -154,5 +154,28 @@ public void Image(Type applicationType, Type themeType, string expected) Assert.NotNull(theme); Assert.Equal(expected, theme?.Image); } + + /// + /// Test the theme mode property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), ThemeMode.Dark)] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), ThemeMode.Light)] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), ThemeMode.Dark)] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), ThemeMode.Light)] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), ThemeMode.Dark)] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), ThemeMode.Light)] + public void Mode(Type applicationType, Type themeType, ThemeMode expected) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); + + Assert.NotNull(theme); + Assert.Equal(expected, theme?.ThemeMode); + } } } diff --git a/src/WebExpress.WebCore.Test/TestThemeA.cs b/src/WebExpress.WebCore.Test/TestThemeA.cs index b46c648..a3b9f0d 100644 --- a/src/WebExpress.WebCore.Test/TestThemeA.cs +++ b/src/WebExpress.WebCore.Test/TestThemeA.cs @@ -9,6 +9,7 @@ namespace WebExpress.WebCore.Test [Name("TestThemeA")] [Description("A dummy theme for testing.")] [Image("webexpress.webcore.test.testthemea.png")] + [ThemeMode(ThemeMode.Dark)] public sealed class TestThemeA : ITheme { /// @@ -17,6 +18,6 @@ public sealed class TestThemeA : ITheme /// /// A string representing the text color in hexadecimal format. /// - public static string TextColor => "000000"; + public static string TextColor => "FFFFFF"; } } diff --git a/src/WebExpress.WebCore.Test/TestThemeB.cs b/src/WebExpress.WebCore.Test/TestThemeB.cs index a4b36ff..41229a9 100644 --- a/src/WebExpress.WebCore.Test/TestThemeB.cs +++ b/src/WebExpress.WebCore.Test/TestThemeB.cs @@ -15,6 +15,6 @@ public sealed class TestThemeB : ITheme /// /// A string representing the text color in hexadecimal format. /// - public static string TextColor => "FFFFFF"; + public static string TextColor => "000000"; } } diff --git a/src/WebExpress.WebCore/WebAttribute/ThemeModeAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ThemeModeAttribute.cs new file mode 100644 index 0000000..8baa04c --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ThemeModeAttribute.cs @@ -0,0 +1,21 @@ +using System; +using WebExpress.WebCore.WebTheme; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Attribute to specify the theme mode. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ThemeModeAttribute : Attribute, IThemeAttribute + { + /// + /// Initializes a new instance of the class with the specified theme mode. + /// + /// The theme mode to be applied. + public ThemeModeAttribute(ThemeMode mode) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs index 103fc68..7bc817a 100644 --- a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs @@ -39,5 +39,10 @@ public interface IThemeContext : IContext /// Returns the description of the theme. /// string Description { get; } + + /// + /// Returns the mode of the theme. + /// + ThemeMode ThemeMode { get; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs index 8580f33..bfe622e 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs @@ -39,5 +39,10 @@ public class ThemeContext : IThemeContext /// Returns the description of the theme. /// public string Description { get; internal set; } + + /// + /// Returns the mode of the theme. + /// + public ThemeMode ThemeMode { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs index 7742069..9576187 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs @@ -154,6 +154,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IThemeAttribute)))) @@ -170,6 +171,17 @@ private void Register(IPluginContext pluginContext, IEnumerable(customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString()); + } + catch + { + mode = ThemeMode.Light; + } + } } // assign the theme to existing applications @@ -182,7 +194,8 @@ private void Register(IPluginContext pluginContext, IEnumerable + /// Specifies the theme mode. + /// + public enum ThemeMode + { + /// + /// Light theme mode. + /// + Light, + + /// + /// Dark theme mode. + /// + Dark + } +} From 9d1251b08948237db5896c26ab076f50e789c0dd Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 21 Mar 2025 17:23:29 +0100 Subject: [PATCH 143/162] add: theme style property in theme context --- .../Manager/UnitTestThemeManager.cs | 23 +++++++++++++++++++ src/WebExpress.WebCore.Test/TestThemeA.cs | 1 + .../WebAttribute/ThemeStyleAttribute.cs | 20 ++++++++++++++++ .../WebTheme/IThemeContext.cs | 5 ++++ .../WebTheme/ThemeContext.cs | 5 ++++ .../WebTheme/ThemeManager.cs | 8 ++++++- 6 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/WebExpress.WebCore/WebAttribute/ThemeStyleAttribute.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs index dd44f1c..0f6e196 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs @@ -177,5 +177,28 @@ public void Mode(Type applicationType, Type themeType, ThemeMode expected) Assert.NotNull(theme); Assert.Equal(expected, theme?.ThemeMode); } + + /// + /// Test the theme style property of the theme. + /// + [Theory] + [InlineData(typeof(TestApplicationA), typeof(TestThemeA), "/server/appa/asserts/css/themea.css")] + [InlineData(typeof(TestApplicationA), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationB), typeof(TestThemeA), "/server/appb/asserts/css/themea.css")] + [InlineData(typeof(TestApplicationB), typeof(TestThemeB), null)] + [InlineData(typeof(TestApplicationC), typeof(TestThemeA), "/server/asserts/css/themea.css")] + [InlineData(typeof(TestApplicationC), typeof(TestThemeB), null)] + public void ThemeStyle(Type applicationType, Type themeType, string expected) + { + // preconditions + var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); + + // test execution + var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); + + Assert.NotNull(theme); + Assert.Equal(expected, theme?.ThemeStyle); + } } } diff --git a/src/WebExpress.WebCore.Test/TestThemeA.cs b/src/WebExpress.WebCore.Test/TestThemeA.cs index a3b9f0d..e1506aa 100644 --- a/src/WebExpress.WebCore.Test/TestThemeA.cs +++ b/src/WebExpress.WebCore.Test/TestThemeA.cs @@ -10,6 +10,7 @@ namespace WebExpress.WebCore.Test [Description("A dummy theme for testing.")] [Image("webexpress.webcore.test.testthemea.png")] [ThemeMode(ThemeMode.Dark)] + [ThemeStyle("/asserts/css/themea.css")] public sealed class TestThemeA : ITheme { /// diff --git a/src/WebExpress.WebCore/WebAttribute/ThemeStyleAttribute.cs b/src/WebExpress.WebCore/WebAttribute/ThemeStyleAttribute.cs new file mode 100644 index 0000000..1235cb6 --- /dev/null +++ b/src/WebExpress.WebCore/WebAttribute/ThemeStyleAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace WebExpress.WebCore.WebAttribute +{ + /// + /// Specifies the style for a theme. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] + public class ThemeStyleAttribute : Attribute, IThemeAttribute + { + /// + /// Initializes a new instance of the class with the specified URI. + /// + /// The URI of the css theme style. + public ThemeStyleAttribute(string uri) + { + + } + } +} diff --git a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs index 7bc817a..6ee7a61 100644 --- a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs @@ -44,5 +44,10 @@ public interface IThemeContext : IContext /// Returns the mode of the theme. /// ThemeMode ThemeMode { get; } + + /// + /// Returns the URI resource for the css theme style. + /// + UriResource ThemeStyle { get; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs index bfe622e..9456cc3 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs @@ -44,5 +44,10 @@ public class ThemeContext : IThemeContext /// Returns the mode of the theme. /// public ThemeMode ThemeMode { get; internal set; } + + /// + /// Returns the URI resource for the css theme style. + /// + public UriResource ThemeStyle { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs index 9576187..ae11b42 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs @@ -155,6 +155,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IThemeAttribute)))) @@ -182,6 +183,10 @@ private void Register(IPluginContext pluginContext, IEnumerable Date: Fri, 28 Mar 2025 15:56:09 +0100 Subject: [PATCH 144/162] refactor: separate route from uri --- .../Fixture/UnitTestFixture.cs | 7 +- .../Manager/UnitTestApplication.cs | 4 +- .../Manager/UnitTestAssetManager.cs | 2 +- .../Manager/UnitTestFragmentManager.cs | 7 +- .../Manager/UnitTestPageManager.cs | 67 ++--- .../Manager/UnitTestPluginManager.cs | 2 +- .../Manager/UnitTestResourceManager.cs | 53 ++-- .../Manager/UnitTestRestApiManager.cs | 41 +-- .../Manager/UnitTestSettingPageManager.cs | 35 ++- .../Manager/UnitTestSitemapManager.cs | 143 ++++----- .../Manager/UnitTestStatusPageManager.cs | 2 +- .../Manager/UnitTestThemeManager.cs | 4 +- .../Route/UnitTestRoute.cs | 99 +++++++ src/WebExpress.WebCore.Test/TestFragmentA.cs | 5 +- src/WebExpress.WebCore.Test/TestParameterA.cs | 39 +++ ...{UnitTestUriRelative.cs => UnitTestUri.cs} | 24 +- .../Uri/UnitTestUriAbsolute.cs | 20 +- .../Uri/UnitTestUriConcat.cs | 31 ++ .../Uri/UnitTestUriRelativeAppend.cs | 49 ---- .../Uri/UnitTestUriRelativeExtendedPath.cs | 10 +- .../Uri/UnitTestUriRelativeSkip.cs | 79 ----- .../Uri/UnitTestUriRelativeTake.cs | 134 --------- .../Uri/UnitTestUriSkip.cs | 33 +++ .../Uri/UnitTestUriTake.cs | 38 +++ .../{TestPageB.cs => WWW/About.cs} | 10 +- .../{ => WWW/Api}/TestRestApiA.cs | 4 +- .../{ => WWW/Api}/TestRestApiB.cs | 5 +- .../{ => WWW/Api}/TestRestApiC.cs | 5 +- .../Home/TestPageC.cs => WWW/Blog/Index.cs} | 8 +- .../{TestPageC.cs => WWW/Blog/Post/Add.cs} | 9 +- .../WWW/Blog/Post/Index.cs | 52 ++++ .../Blog/Post/PostId/Edit.cs} | 10 +- .../WWW/Blog/Post/PostId/Index.cs | 58 ++++ .../WWW/Blog/Post/PostId/SegmentInfo.cs | 10 + src/WebExpress.WebCore.Test/WWW/Contact.cs | 52 ++++ src/WebExpress.WebCore.Test/WWW/Index.cs | 58 ++++ .../WWW/Products/Details/Index.cs | 59 ++++ .../WWW/Products/Index.cs | 58 ++++ .../WWW/Products/List.cs | 58 ++++ .../{ => WWW/Resources}/TestResourceA.cs | 10 +- .../{ => WWW/Resources}/TestResourceB.cs | 9 +- .../{ => WWW/Resources}/TestResourceC.cs | 10 +- .../{ => WWW/Resources}/TestResourceD.cs | 9 +- .../Settings}/TestSettingCategoryA.cs | 2 +- .../Settings}/TestSettingCategoryB.cs | 2 +- .../Settings}/TestSettingCategoryC.cs | 2 +- .../{ => WWW/Settings}/TestSettingGroupA.cs | 2 +- .../{ => WWW/Settings}/TestSettingGroupB.cs | 2 +- .../{ => WWW/Settings}/TestSettingGroupC.cs | 2 +- .../{ => WWW/Settings}/TestSettingPageA.cs | 4 +- .../{ => WWW/Settings}/TestSettingPageB.cs | 4 +- .../{ => WWW/Settings}/TestSettingPageC.cs | 4 +- .../WebExpress.WebCore.Test.csproj | 2 +- .../Config/HttpServerConfig.cs | 6 +- src/WebExpress.WebCore/HttpServer.cs | 12 +- src/WebExpress.WebCore/HttpServerContext.cs | 16 +- src/WebExpress.WebCore/IHttpServerContext.cs | 8 +- .../Internationalization/de | 8 +- .../Internationalization/en | 8 +- .../WebApplication/ApplicationContext.cs | 8 +- .../WebApplication/ApplicationManager.cs | 6 +- .../WebApplication/IApplicationContext.cs | 6 +- .../WebAsset/AssetContext.cs | 20 +- .../WebAsset/AssetManager.cs | 4 +- .../WebAttribute/ParentAttribute.cs | 21 -- .../WebEndpoint/EndpointManager.cs | 110 ++++++- .../WebEndpoint/IEndpointContext.cs | 17 +- src/WebExpress.WebCore/WebEndpoint/IRoute.cs | 48 ++++ .../WebEndpoint/RouteEndpoint.cs | 269 +++++++++++++++++ src/WebExpress.WebCore/WebEx.cs | 6 +- .../WebMessage/Parameter.cs | 8 +- src/WebExpress.WebCore/WebMessage/Request.cs | 4 +- .../WebPage/IPageContext.cs | 5 - .../WebPage/IVisualTreeContext.cs | 2 +- .../WebPage/Model/PageItem.cs | 55 +--- src/WebExpress.WebCore/WebPage/PageContext.cs | 11 +- src/WebExpress.WebCore/WebPage/PageManager.cs | 41 ++- .../WebPage/RenderContext.cs | 2 +- .../WebPage/VisualTreeContext.cs | 2 +- .../WebPlugin/IPluginContext.cs | 9 +- .../WebPlugin/PluginContext.cs | 9 +- .../WebPlugin/PluginManager.cs | 10 +- .../WebResource/Model/ResourceItem.cs | 36 ++- .../WebResource/ResourceAsset.cs | 1 - .../WebResource/ResourceContext.cs | 51 +--- .../WebResource/ResourceFile.cs | 3 +- .../WebResource/ResourceManager.cs | 46 ++- .../WebRestAPI/Model/RestApiItem.cs | 43 +-- .../WebRestAPI/RestApiContext.cs | 45 +-- .../WebRestAPI/RestApiManager.cs | 82 +++--- .../WebSettingPage/Model/SettingPageItem.cs | 42 +-- .../WebSettingPage/SettingPageManager.cs | 55 ++-- .../WebSitemap/ISitemapManager.cs | 39 ++- .../WebSitemap/SearchResult.cs | 2 +- .../WebSitemap/SitemapManager.cs | 77 ++--- .../WebStatusPage/IStatusPageContext.cs | 4 +- .../WebStatusPage/StatusPageContext.cs | 4 +- .../WebStatusPage/StatusPageManager.cs | 3 +- .../WebTheme/IThemeContext.cs | 8 +- .../WebTheme/ThemeContext.cs | 8 +- .../WebTheme/ThemeManager.cs | 6 +- src/WebExpress.WebCore/WebUri/IUri.cs | 137 +++++++++ .../WebUri/{UriResource.cs => UriEndpoint.cs} | 271 +++++++++--------- src/WebExpress.WebCore/WebUri/UriFragment.cs | 2 +- 104 files changed, 1939 insertions(+), 1205 deletions(-) create mode 100644 src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs create mode 100644 src/WebExpress.WebCore.Test/TestParameterA.cs rename src/WebExpress.WebCore.Test/Uri/{UnitTestUriRelative.cs => UnitTestUri.cs} (81%) create mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs create mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs create mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs rename src/WebExpress.WebCore.Test/{TestPageB.cs => WWW/About.cs} (88%) rename src/WebExpress.WebCore.Test/{ => WWW/Api}/TestRestApiA.cs (95%) rename src/WebExpress.WebCore.Test/{ => WWW/Api}/TestRestApiB.cs (93%) rename src/WebExpress.WebCore.Test/{ => WWW/Api}/TestRestApiC.cs (94%) rename src/WebExpress.WebCore.Test/{Web/Home/TestPageC.cs => WWW/Blog/Index.cs} (89%) rename src/WebExpress.WebCore.Test/{TestPageC.cs => WWW/Blog/Post/Add.cs} (87%) create mode 100644 src/WebExpress.WebCore.Test/WWW/Blog/Post/Index.cs rename src/WebExpress.WebCore.Test/{TestPageA.cs => WWW/Blog/Post/PostId/Edit.cs} (86%) create mode 100644 src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Contact.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Index.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Products/Details/Index.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Products/Index.cs create mode 100644 src/WebExpress.WebCore.Test/WWW/Products/List.cs rename src/WebExpress.WebCore.Test/{ => WWW/Resources}/TestResourceA.cs (81%) rename src/WebExpress.WebCore.Test/{ => WWW/Resources}/TestResourceB.cs (79%) rename src/WebExpress.WebCore.Test/{ => WWW/Resources}/TestResourceC.cs (84%) rename src/WebExpress.WebCore.Test/{ => WWW/Resources}/TestResourceD.cs (85%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingCategoryA.cs (89%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingCategoryB.cs (89%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingCategoryC.cs (89%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingGroupA.cs (90%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingGroupB.cs (90%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingGroupC.cs (88%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingPageA.cs (93%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingPageB.cs (93%) rename src/WebExpress.WebCore.Test/{ => WWW/Settings}/TestSettingPageC.cs (93%) delete mode 100644 src/WebExpress.WebCore/WebAttribute/ParentAttribute.cs create mode 100644 src/WebExpress.WebCore/WebEndpoint/IRoute.cs create mode 100644 src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs create mode 100644 src/WebExpress.WebCore/WebUri/IUri.cs rename src/WebExpress.WebCore/WebUri/{UriResource.cs => UriEndpoint.cs} (67%) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index 3f1d031..a275199 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -6,6 +6,7 @@ using System.Text; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebPage; @@ -36,13 +37,13 @@ public static IHttpServerContext CreateHttpServerContextMock() { return new HttpServerContext ( - "localhost", + new RouteEndpoint("localhost"), [], "", Environment.CurrentDirectory, Environment.CurrentDirectory, Environment.CurrentDirectory, - new UriResource("/server"), + new RouteEndpoint("/server"), CultureInfo.GetCultureInfo("en"), new Log() { LogMode = LogMode.Off }, null @@ -102,7 +103,7 @@ public static Request CrerateRequestMock(string content = "", string uri = "") if (!string.IsNullOrEmpty(uri)) { - request.Uri = new UriResource(uri); + request.Uri = new UriEndpoint(uri); } return request; diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs index 9f69262..59c4530 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestApplication.cs @@ -112,7 +112,7 @@ public void Icon(Type applicationType, string icon) var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution - Assert.Equal(icon, application.Icon); + Assert.Equal(icon, application.Icon.ToString()); } /// @@ -129,7 +129,7 @@ public void ContextPath(Type applicationType, string contextPath) var application = componentHub.ApplicationManager.GetApplications(applicationType).FirstOrDefault(); // test execution - Assert.Equal(contextPath, application.ContextPath); + Assert.Equal(contextPath, application.ContextPath.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs index 9f8a9df..9b39472 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestAssetManager.cs @@ -88,7 +88,7 @@ public void Uri(Type applicationType, string uri) var asset = componentHub.AssetManager.GetAssets(application)?.FirstOrDefault(x => x.EndpointId.ToString() == Path.GetFileName(uri)); // test execution - Assert.Equal(uri, asset?.Uri); + Assert.Equal(uri, asset?.Route.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs index 82b411b..35df62c 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestFragmentManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.WWW; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebFragment; using WebExpress.WebCore.WebPage; @@ -92,9 +93,9 @@ public void Id(Type applicationType, Type fragmentType, string id) [Theory] [InlineData(typeof(TestApplicationA), typeof(IScope), 0)] [InlineData(typeof(TestApplicationA), typeof(TestScopeA), 1)] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), 1)] + [InlineData(typeof(TestApplicationA), typeof(About), 1)] [InlineData(typeof(TestApplicationB), typeof(IScope), 0)] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), 1)] + [InlineData(typeof(TestApplicationB), typeof(About), 1)] public void GetFragments(Type applicationType, Type scopeType, int count) { // preconditions @@ -115,7 +116,7 @@ public void GetFragments(Type applicationType, Type scopeType, int count) [Theory] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeA), false)] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeB), false)] - [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestPageB), false)] + [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(About), false)] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(IScope), true)] [InlineData(typeof(TestApplicationA), typeof(TestSectionA), typeof(TestScopeD), true)] public void Render(Type applicationType, Type sectionType, Type scopeType, bool empty) diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs index 09e71ac..fc39f30 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPageManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.WWW; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebPage; @@ -20,7 +21,7 @@ public void Register() var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); // test execution - Assert.Equal(12, componentHub.PageManager.Pages.Count()); + Assert.Equal(33, componentHub.PageManager.Pages.Count()); } /// @@ -44,21 +45,23 @@ public void Remove() /// Test the id property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - [InlineData(typeof(TestApplicationA), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - [InlineData(typeof(TestApplicationB), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - [InlineData(typeof(TestApplicationB), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - [InlineData(typeof(TestApplicationC), typeof(TestPageA), "webexpress.webcore.test.testpagea")] - [InlineData(typeof(TestApplicationC), typeof(TestPageB), "webexpress.webcore.test.testpageb")] - [InlineData(typeof(TestApplicationC), typeof(TestPageC), "webexpress.webcore.test.testpagec")] - public void Id(Type applicationType, Type resourceType, string id) + [InlineData(typeof(TestApplicationA), typeof(WWW.Index), "webexpress.webcore.test.www.index")] + [InlineData(typeof(TestApplicationA), typeof(About), "webexpress.webcore.test.www.about")] + [InlineData(typeof(TestApplicationA), typeof(Contact), "webexpress.webcore.test.www.contact")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Index), "webexpress.webcore.test.www.blog.index")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.Index), "webexpress.webcore.test.www.blog.post.index")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.Add), "webexpress.webcore.test.www.blog.post.add")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Edit), "webexpress.webcore.test.www.blog.post.postid.edit")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Index), "webexpress.webcore.test.www.blog.post.postid.index")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Index), "webexpress.webcore.test.www.products.index")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.List), "webexpress.webcore.test.www.products.list")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), "webexpress.webcore.test.www.products.details.index")] + public void Id(Type applicationType, Type pageType, string id) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); - var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); + var page = componentHub.PageManager.GetPages(pageType, application)?.FirstOrDefault(); // test execution Assert.Equal(id, page.EndpointId.ToString()); @@ -68,15 +71,15 @@ public void Id(Type applicationType, Type resourceType, string id) /// Test the title property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestPageA), "webindex:pagea.label")] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), "webindex:pageb.label")] - [InlineData(typeof(TestApplicationA), typeof(TestPageC), "webindex:pagec.label")] - [InlineData(typeof(TestApplicationB), typeof(TestPageA), "webindex:pagea.label")] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), "webindex:pageb.label")] - [InlineData(typeof(TestApplicationB), typeof(TestPageC), "webindex:pagec.label")] - [InlineData(typeof(TestApplicationC), typeof(TestPageA), "webindex:pagea.label")] - [InlineData(typeof(TestApplicationC), typeof(TestPageB), "webindex:pageb.label")] - [InlineData(typeof(TestApplicationC), typeof(TestPageC), "webindex:pagec.label")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Index), "webindex:home.label")] + [InlineData(typeof(TestApplicationA), typeof(About), "webindex:about.label")] + [InlineData(typeof(TestApplicationA), typeof(Contact), "webindex:contact.label")] + [InlineData(typeof(TestApplicationB), typeof(WWW.Index), "webindex:home.label")] + [InlineData(typeof(TestApplicationB), typeof(About), "webindex:about.label")] + [InlineData(typeof(TestApplicationB), typeof(Contact), "webindex:contact.label")] + [InlineData(typeof(TestApplicationC), typeof(WWW.Index), "webindex:home.label")] + [InlineData(typeof(TestApplicationC), typeof(About), "webindex:about.label")] + [InlineData(typeof(TestApplicationC), typeof(Contact), "webindex:contact.label")] public void Title(Type applicationType, Type resourceType, string title) { @@ -93,16 +96,16 @@ public void Title(Type applicationType, Type resourceType, string title) /// Test the context path property of the page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestPageA), "/server/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestPageB), "/server/appa/resa/b")] - [InlineData(typeof(TestApplicationA), typeof(TestPageC), "/server/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestPageA), "/server/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestPageB), "/server/appb/resa/b")] - [InlineData(typeof(TestApplicationB), typeof(TestPageC), "/server/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestPageA), "/server")] - [InlineData(typeof(TestApplicationC), typeof(TestPageB), "/server/resa/b")] - [InlineData(typeof(TestApplicationC), typeof(TestPageC), "/server")] - public void ContextPath(Type applicationType, Type resourceType, string path) + [InlineData(typeof(TestApplicationA), typeof(WWW.Index), "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(About), "/server/appa/about")] + [InlineData(typeof(TestApplicationA), typeof(Contact), "/server/appa/contact")] + [InlineData(typeof(TestApplicationB), typeof(WWW.Index), "/server/appb")] + [InlineData(typeof(TestApplicationB), typeof(About), "/server/appb/about")] + [InlineData(typeof(TestApplicationB), typeof(Contact), "/server/appb/contact")] + [InlineData(typeof(TestApplicationC), typeof(WWW.Index), "/server")] + [InlineData(typeof(TestApplicationC), typeof(About), "/server/about")] + [InlineData(typeof(TestApplicationC), typeof(Contact), "/server/contact")] + public void RoutePath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -110,7 +113,7 @@ public void ContextPath(Type applicationType, Type resourceType, string path) var page = componentHub.PageManager.GetPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(path, page.ContextPath); + Assert.Equal(path, page.Route.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs index ae04338..2b1d5d7 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestPluginManager.cs @@ -175,7 +175,7 @@ public void GetIcon() var plugin = componentHub.PluginManager.GetPlugin(typeof(TestPlugin)); // test execution - Assert.Equal("/server/assets/img/Logo.png", plugin.Icon); + Assert.Equal("/server/assets/img/Logo.png", plugin.Icon.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs index 5a05211..7a6728d 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestResourceManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.WWW.Resources; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebResource; @@ -44,18 +45,18 @@ public void Remove() /// Test the id property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "webexpress.webcore.test.testresourcea")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "webexpress.webcore.test.testresourceb")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "webexpress.webcore.test.testresourcec")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "webexpress.webcore.test.testresourced")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "webexpress.webcore.test.www.resources.testresourced")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "webexpress.webcore.test.www.resources.testresourced")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "webexpress.webcore.test.www.resources.testresourced")] public void Id(Type applicationType, Type resourceType, string id) { // preconditions @@ -71,20 +72,20 @@ public void Id(Type applicationType, Type resourceType, string id) /// Test the context path property of the resource. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "/server/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "/server/appa/resa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "/server/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "/server/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "/server/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "/server/appb/resa")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "/server/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "/server/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "/server")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "/server/resa")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/server")] - [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/server")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), "/server/appa/resources/testresourcea")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), "/server/appa/resources/testresourceb")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), "/server/appa/resources/testresourcec")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), "/server/appa/resources/testresourced")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceA), "/server/appb/resources/testresourcea")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceB), "/server/appb/resources/testresourceb")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceC), "/server/appb/resources/testresourcec")] + [InlineData(typeof(TestApplicationB), typeof(TestResourceD), "/server/appb/resources/testresourced")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceA), "/server/resources/testresourcea")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceB), "/server/resources/testresourceb")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceC), "/server/resources/testresourcec")] + [InlineData(typeof(TestApplicationC), typeof(TestResourceD), "/server/resources/testresourced")] - public void ContextPath(Type applicationType, Type resourceType, string path) + public void RoutePath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -92,7 +93,7 @@ public void ContextPath(Type applicationType, Type resourceType, string path) var resource = componentHub.ResourceManager.GetResorces(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(path, resource.ContextPath); + Assert.Equal(path, resource.Route.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 03bd2f9..769f3bb 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.WWW.Api; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebRestApi; @@ -44,15 +45,15 @@ public void Remove() /// Test the id property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "webexpress.webcore.test.testrestapia")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "webexpress.webcore.test.testrestapib")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "webexpress.webcore.test.testrestapic")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] public void Id(Type applicationType, Type resourceType, string id) { // preconditions @@ -68,16 +69,16 @@ public void Id(Type applicationType, Type resourceType, string id) /// Test the context path property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "/server/appa/1")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "/server/appa/1/apia/2")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "/server/appa/1/apia/2/apib/3")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "/server/appb/1")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "/server/appb/1/apia/2")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "/server/appb/1/apia/2/apib/3")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/server/1")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/server/1/apia/2")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/server/1/apia/2/apib/3")] - public void ContextPath(Type applicationType, Type resourceType, string path) + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "/server/appa/api/1/testrestapia")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "/server/appa/api/2/testrestapib")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "/server/appa/api/3/testrestapic")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "/server/appb/api/1/testrestapia")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "/server/appb/api/2/testrestapib")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "/server/appb/api/3/testrestapic")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "/server/api/1/testrestapia")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "/server/api/2/testrestapib")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "/server/api/3/testrestapic")] + public void RoutePath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -85,7 +86,7 @@ public void ContextPath(Type applicationType, Type resourceType, string path) var api = componentHub.RestApiManager.GetRestApi(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(path, api?.ContextPath); + Assert.Equal(path, api?.Route.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs index bbc38a0..bab64fb 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSettingPageManager.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.Test.Fixture; +using WebExpress.WebCore.Test.WWW.Settings; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSettingPage; @@ -70,12 +71,15 @@ public void Remove() /// Test the id property of the setting page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webexpress.webcore.test.testsettingpagea")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webexpress.webcore.test.testsettingpageb")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "webexpress.webcore.test.www.settings.testsettingpagea")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "webexpress.webcore.test.www.settings.testsettingpageb")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageC), "webexpress.webcore.test.www.settings.testsettingpagec")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "webexpress.webcore.test.www.settings.testsettingpagea")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "webexpress.webcore.test.www.settings.testsettingpageb")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageC), "webexpress.webcore.test.www.settings.testsettingpagec")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "webexpress.webcore.test.www.settings.testsettingpagea")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "webexpress.webcore.test.www.settings.testsettingpageb")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageC), "webexpress.webcore.test.www.settings.testsettingpagec")] public void Id(Type applicationType, Type resourceType, string id) { // preconditions @@ -112,13 +116,16 @@ public void Title(Type applicationType, Type resourceType, string title) /// Test the context path property of the setting page. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "/server/appa")] - [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "/server/appa")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "/server/appb")] - [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/server/appb")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/server")] - [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/server")] - public void ContextPath(Type applicationType, Type resourceType, string path) + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageA), "/server/appa/settings/testsettingpagea")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageB), "/server/appa/settings/testsettingpageb")] + [InlineData(typeof(TestApplicationA), typeof(TestSettingPageC), "/server/appa/settings/testsettingpagec")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageA), "/server/appb/settings/testsettingpagea")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageB), "/server/appb/settings/testsettingpageb")] + [InlineData(typeof(TestApplicationB), typeof(TestSettingPageC), "/server/appb/settings/testsettingpagec")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageA), "/server/settings/testsettingpagea")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageB), "/server/settings/testsettingpageb")] + [InlineData(typeof(TestApplicationC), typeof(TestSettingPageC), "/server/settings/testsettingpagec")] + public void RoutePath(Type applicationType, Type resourceType, string path) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); @@ -126,7 +133,7 @@ public void ContextPath(Type applicationType, Type resourceType, string path) var settingPage = componentHub.SettingPageManager.GetSettingPages(resourceType, application)?.FirstOrDefault(); // test execution - Assert.Equal(path, settingPage.ContextPath); + Assert.Equal(path, settingPage.Route.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 59d5a25..49a9d7f 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,5 +1,6 @@ using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.Test.Web.Home; +using WebExpress.WebCore.Test.WWW.Api; +using WebExpress.WebCore.Test.WWW.Resources; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSitemap; using WebExpress.WebCore.WebUri; @@ -24,38 +25,37 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(58, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(82, componentManager.SitemapManager.SiteMap.Count()); } /// /// Test the search resource function of the sitemap. /// [Theory] - [InlineData("http://localhost:8080/server/appa/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/appa/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/appa/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/appa/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/appb/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/appb/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/appb/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/appb/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appa/resa/B/testpageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appb/resa/b/testpageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/resa/b/TestPageB", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] - [InlineData("http://localhost:8080/server/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] - [InlineData("http://localhost:8080/server/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] + [InlineData("http://localhost:8080/server/appa/resources/testresourcea", "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData("http://localhost:8080/server/appa/resources/testresourceb", "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData("http://localhost:8080/server/appa/resources/testresourcec", "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData("http://localhost:8080/server/appa/resources/testresourced", "webexpress.webcore.test.www.resources.testresourced")] + [InlineData("http://localhost:8080/server/appb/resources/testresourcea", "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData("http://localhost:8080/server/appb/resources/testresourceb", "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData("http://localhost:8080/server/appb/resources/testresourcec", "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData("http://localhost:8080/server/appb/resources/testresourced", "webexpress.webcore.test.www.resources.testresourced")] + [InlineData("http://localhost:8080/server/resources/testresourcea", "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData("http://localhost:8080/server/resources/testresourceb", "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData("http://localhost:8080/server/resources/testresourcec", "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData("http://localhost:8080/server/resources/testresourced", "webexpress.webcore.test.www.resources.testresourced")] + [InlineData("http://localhost:8080/server/appa", "webexpress.webcore.test.www.index")] + [InlineData("http://localhost:8080/server/appa/about", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appa/contact", "webexpress.webcore.test.www.contact")] + [InlineData("http://localhost:8080/server/appb", "webexpress.webcore.test.www.index")] + [InlineData("http://localhost:8080/server/appb/about", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appb/contact", "webexpress.webcore.test.www.contact")] + [InlineData("http://localhost:8080/server", "webexpress.webcore.test.www.index")] + [InlineData("http://localhost:8080/server/about", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/contact", "webexpress.webcore.test.www.contact")] + [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api.testrestapia")] + [InlineData("http://localhost:8080/server/appa/api/2/testrestapib", "webexpress.webcore.test.www.api.testrestapib")] + [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api.testrestapic")] [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] @@ -89,22 +89,36 @@ public void SearchResource(string uri, string id) /// Test the get uri function of the sitemap. /// [Theory] - [InlineData(typeof(TestResourceA), "/server/appa/resa")] - [InlineData(typeof(TestResourceB), "/server/appa/resa/resb")] - [InlineData(typeof(TestResourceC), "/server/appa/resc")] - [InlineData(typeof(TestResourceD), "/server/appa/resd")] - [InlineData(typeof(TestPageA), "/server/appa/pagea")] - [InlineData(typeof(TestPageB), "/server/appa/resa/b/testpageb")] - [InlineData(typeof(TestPageC), "/server")] - [InlineData(typeof(TestPageH), "/server/appa/web/home/testpageh")] - public void GetUri(Type resourceType, string expected) + [InlineData(typeof(TestApplicationA), typeof(TestResourceA), null, "/server/appa/resources/testresourcea")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceB), null, "/server/appa/resources/testresourceb")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceC), null, "/server/appa/resources/testresourcec")] + [InlineData(typeof(TestApplicationA), typeof(TestResourceD), null, "/server/appa/resources/testresourced")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), null, "/server/appa/api/1/testrestapia")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), null, "/server/appa/api/2/testrestapib")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), null, "/server/appa/api/3/testrestapic")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Index), null, "/server/appa")] + [InlineData(typeof(TestApplicationA), typeof(WWW.About), null, "/server/appa/about")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Contact), null, "/server/appa/contact")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Index), null, "/server/appa/blog")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.Index), null, "/server/appa/blog/post")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.Add), null, "/server/appa/blog/post/add")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Index), null, "/server/appa/blog/post/${testparametera}")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Edit), null, "/server/appa/blog/post/${testparametera}/edit")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Index), 1, "/server/appa/blog/post/1")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Edit), 1, "/server/appa/blog/post/1/edit")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Index), null, "/server/appa/products")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.List), null, "/server/appa/products/list")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), null, "/server/appa/products/details/${testparametera}")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), 2, "/server/appa/products/details/2")] + public void GetUri(Type applicationType, Type resourceType, int? param, string expected) { // preconditions var componentHub = UnitTestFixture.CreateAndRegisterComponentHubMock(); + var application = componentHub.ApplicationManager.GetApplications(applicationType)?.FirstOrDefault(); componentHub.SitemapManager.Refresh(); // test execution - var uri = componentHub.SitemapManager.GetUri(resourceType); + var uri = componentHub.SitemapManager.GetUri(resourceType, application, [param.HasValue ? new TestParameterA(param.Value) : null]); Assert.Equal(expected, uri?.ToString()); } @@ -113,38 +127,39 @@ public void GetUri(Type resourceType, string expected) /// Test the get endpoint function of the sitemap. /// [Theory] - [InlineData("http://localhost:8080/server/appa/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/appa/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/appa/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/appa/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/appb/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/appb/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/appb/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/appb/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/resa", "webexpress.webcore.test.testresourcea")] - [InlineData("http://localhost:8080/server/resa/resb", "webexpress.webcore.test.testresourceb")] - [InlineData("http://localhost:8080/server/resc", "webexpress.webcore.test.testresourcec")] - [InlineData("http://localhost:8080/server/resd", "webexpress.webcore.test.testresourced")] - [InlineData("http://localhost:8080/server/appa/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appa/resa/B/testpageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/appb/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/appb/resa/b/TestPageB", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server/appb/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/pagea", "webexpress.webcore.test.testpagea")] - [InlineData("http://localhost:8080/server/resa/b/testpageb", "webexpress.webcore.test.testpageb")] - [InlineData("http://localhost:8080/server", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/", "webexpress.webcore.test.testpagec")] - [InlineData("http://localhost:8080/server/appa/1/apia", "webexpress.webcore.test.testrestapia")] - [InlineData("http://localhost:8080/server/appa/1/apia/2/apib", "webexpress.webcore.test.testrestapib")] - [InlineData("http://localhost:8080/server/appa/1/apia/2/apib/3/apic", "webexpress.webcore.test.testrestapic")] + [InlineData("http://localhost:8080/uri/does/not/exist", null)] + [InlineData("http://localhost:8080/server/appa/resources/testresourcea", "webexpress.webcore.test.www.resources.testresourcea")] + [InlineData("http://localhost:8080/server/appa/resources/testresourceb", "webexpress.webcore.test.www.resources.testresourceb")] + [InlineData("http://localhost:8080/server/appa/resources/testresourcec", "webexpress.webcore.test.www.resources.testresourcec")] + [InlineData("http://localhost:8080/server/appa/resources/testresourced", "webexpress.webcore.test.www.resources.testresourced")] [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/css.mycss.css", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/uri/does/not/exist", null)] + [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api.testrestapia")] + [InlineData("http://localhost:8080/server/appa/api/2/TestRestApiB", "webexpress.webcore.test.www.api.testrestapib")] + [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api.testrestapic")] + [InlineData("http://localhost:8080/server/appa", "webexpress.webcore.test.www.index")] + [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.www.index")] + [InlineData("http://localhost:8080/server/appa/about", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appa/About", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appa/about/", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appa/About/", "webexpress.webcore.test.www.about")] + [InlineData("http://localhost:8080/server/appa/contact", "webexpress.webcore.test.www.contact")] + [InlineData("http://localhost:8080/server/appa/blog", "webexpress.webcore.test.www.blog.index")] + [InlineData("http://localhost:8080/server/appa/blog/post", "webexpress.webcore.test.www.blog.post.index")] + [InlineData("http://localhost:8080/server/appa/blog/post/add", "webexpress.webcore.test.www.blog.post.add")] + [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123/edit", "webexpress.webcore.test.www.blog.post.postid.edit")] + [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123", "webexpress.webcore.test.www.blog.post.postid.index")] + [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123/", "webexpress.webcore.test.www.blog.post.postid.index")] + [InlineData("http://localhost:8080/server/appa/Products/", "webexpress.webcore.test.www.products.index")] + [InlineData("http://localhost:8080/server/appa/Products/Index", "webexpress.webcore.test.www.products.index")] + [InlineData("http://localhost:8080/server/appa/Products/list", "webexpress.webcore.test.www.products.list")] + [InlineData("http://localhost:8080/server/appa/products/details", "webexpress.webcore.test.www.products.details.index")] + [InlineData("http://localhost:8080/server/appa/products/details/10E96737-5C72-4C25-9E74-F96D8863D123", "webexpress.webcore.test.www.products.details.index")] + [InlineData("http://localhost:8080/server/appa/products/details/10E96737-5C72-4C25-9E74-F96D8863D123/", "webexpress.webcore.test.www.products.details.index")] public void GetEndpoint(string uri, string expected) { // preconditions @@ -152,7 +167,7 @@ public void GetEndpoint(string uri, string expected) componentHub.SitemapManager.Refresh(); // test execution - var endpoint = componentHub.SitemapManager.GetEndpoint(new UriResource(uri)); + var endpoint = componentHub.SitemapManager.GetEndpoint(new UriEndpoint(uri)); Assert.Equal(expected, endpoint?.EndpointId?.ToString()); } diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs index 22765b8..56ded64 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestStatusPageManager.cs @@ -138,7 +138,7 @@ public void Icon(Type applicationType, Type statusPageType, string icon) var statusPage = componentHub.StatusPageManager.GetStatusPage(application, statusPageType); // test execution - Assert.Equal(icon, statusPage?.StatusIcon); + Assert.Equal(icon, statusPage?.StatusIcon?.ToString()); } /// diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs index 0f6e196..d7300d8 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestThemeManager.cs @@ -152,7 +152,7 @@ public void Image(Type applicationType, Type themeType, string expected) var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); Assert.NotNull(theme); - Assert.Equal(expected, theme?.Image); + Assert.Equal(expected, theme?.Image?.ToString()); } /// @@ -198,7 +198,7 @@ public void ThemeStyle(Type applicationType, Type themeType, string expected) var theme = componentHub.ThemeManager.GetThemes(application, themeType).FirstOrDefault(); Assert.NotNull(theme); - Assert.Equal(expected, theme?.ThemeStyle); + Assert.Equal(expected, theme?.ThemeStyle?.ToString()); } } } diff --git a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs new file mode 100644 index 0000000..f29a458 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs @@ -0,0 +1,99 @@ +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.Test.Route +{ + /// + /// Tests the route. + /// + [Collection("NonParallelTests")] + public class UnitTestRoute + { + /// + /// Test the concat method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", "", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void Concat(string baseRoute, string segment, string expected, int count) + { + // preconditions + var uri = new RouteEndpoint(baseRoute); + + // test execution + var concat = uri.Concat(segment); + + Assert.Equal(expected, concat.ToString()); + Assert.Equal(count, concat.PathSegments.Count()); + } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", "", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void CombinePath(string baseRoute, string pathB, string expected, int count) + { + // test execution + var combine = RouteEndpoint.Combine([new RouteEndpoint(baseRoute), new RouteEndpoint(pathB)]); + + Assert.Equal(expected, combine.ToString()); + Assert.Equal(count, combine.PathSegments.Count()); + } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", "", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void CombineRoute(string baseRoute, string pathB, string expected, int count) + { + // test execution + var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), [pathB]); + + Assert.Equal(expected, combine.ToString()); + Assert.Equal(count, combine.PathSegments.Count()); + } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", "", null, "/a/b/c")] + [InlineData("/a/b/c", "", "", "/a/b/c")] + [InlineData("/a/b/c", "", "d", "/a/b/c/d")] + [InlineData("/a/b/c", "", "/d/e/f", "/a/b/c/d/e/f")] + public void CombineSegment(string baseRoute, string intermediateRoute, string segment, string expected) + { + // test execution + var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), new RouteEndpoint(intermediateRoute), new UriPathSegmentConstant(segment)); + + Assert.Equal(expected, combine.ToString()); + } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", "", null, "/a/b/c")] + [InlineData("/a/b/c", "", new[] { "" }, "/a/b/c")] + [InlineData("/a/b/c", "", new[] { "d" }, "/a/b/c/d")] + [InlineData("/a/b/c", "", new[] { "d", "e", "f" }, "/a/b/c/d/e/f")] + public void CombineSegments(string baseRoute, string intermediateRoute, string[] segments, string expected) + { + // test execution + var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), new RouteEndpoint(intermediateRoute), segments + ?.Select(x => new UriPathSegmentConstant(x)) ?? []); + + Assert.Equal(expected, combine.ToString()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestFragmentA.cs b/src/WebExpress.WebCore.Test/TestFragmentA.cs index 0418aeb..cfba253 100644 --- a/src/WebExpress.WebCore.Test/TestFragmentA.cs +++ b/src/WebExpress.WebCore.Test/TestFragmentA.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.Test.WWW; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebFragment; using WebExpress.WebCore.WebHtml; @@ -11,7 +12,7 @@ namespace WebExpress.WebCore.Test /// [Section()] [Scope] - [Scope] + [Scope] [Order(0)] public sealed class TestFragmentA : IFragment { diff --git a/src/WebExpress.WebCore.Test/TestParameterA.cs b/src/WebExpress.WebCore.Test/TestParameterA.cs new file mode 100644 index 0000000..e77468b --- /dev/null +++ b/src/WebExpress.WebCore.Test/TestParameterA.cs @@ -0,0 +1,39 @@ +using WebExpress.WebCore.WebMessage; + +namespace WebExpress.WebCore.Test +{ + /// + /// Represents a test parameter. + /// + internal class TestParameterA : Parameter + { + /// + /// Initializes a new instance of the class. + /// + public TestParameterA() + : base("TestParameterA", null, ParameterScope.Url) + { + + } + + /// + /// Initializes a new instance of the class with a specified value. + /// + /// The value of the parameter. + public TestParameterA(int value) + : base("TestParameterA", value, ParameterScope.Url) + { + + } + + /// + /// Initializes a new instance of the class with a specified value. + /// + /// The value of the parameter. + public TestParameterA(Guid value) + : base("TestParameterA", value.ToString(), ParameterScope.Url) + { + + } + } +} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs similarity index 81% rename from src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs rename to src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs index 8d19ac5..04e8c1b 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelative.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs @@ -3,22 +3,22 @@ namespace WebExpress.WebCore.Test.Uri { /// - /// Tests an relative uri. + /// Tests an uri. /// [Collection("NonParallelTests")] - public class UnitTestUriRelative + public class UnitTestUri { [Fact] public void Test_0() { var str = "/abc#a?b=1&c=2"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( uri.ToString() == str && uri.Scheme == UriScheme.Http && - uri.PathSegments.Count == 2 && + uri.PathSegments.Count() == 2 && uri.Fragment == "a" && uri.Query.FirstOrDefault()?.Key == "b" && uri.Query.FirstOrDefault()?.Value == "1" && @@ -32,13 +32,13 @@ public void Test_0() public void Test_1() { var str = "/assets/img/vila.svg"; - var uri = new UriResource("/assets/img/vila.svg"); + var uri = new UriEndpoint("/assets/img/vila.svg"); Assert.True ( uri.ToString() == str && uri.Scheme == UriScheme.Http && - uri.PathSegments.Count == 4 && + uri.PathSegments.Count() == 4 && uri.Fragment == null && uri.Query.Any() == false && uri.IsRelative @@ -49,14 +49,14 @@ public void Test_1() public void Test_2() { var str = "/"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( uri.ToString() == str && uri.Scheme == UriScheme.Http && uri.Authority == null && - uri.PathSegments.Count == 0 && + uri.PathSegments.Count() == 0 && uri.Fragment == null && uri.Query.Any() == false && uri.IsRelative @@ -67,14 +67,14 @@ public void Test_2() public void Test_3() { var str = "/?b=1&c=2"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( uri.ToString() == str && uri.Scheme == UriScheme.Http && uri.Authority == null && - uri.PathSegments.Count == 1 && + uri.PathSegments.Count() == 1 && uri.Query.FirstOrDefault()?.Key == "b" && uri.Query.FirstOrDefault()?.Value == "1" && uri.Query.LastOrDefault()?.Key == "c" && @@ -87,14 +87,14 @@ public void Test_3() public void Test_4() { var str = ""; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( uri.ToString() == str + "/" && uri.Scheme == UriScheme.Http && uri.Authority == null && - uri.PathSegments.Count == 0 && + uri.PathSegments.Count() == 0 && uri.Fragment == null && uri.Query.Any() == false && uri.IsRelative diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs index 38068af..fcb8da2 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs @@ -12,7 +12,7 @@ public class UnitTestUriAbsolute public void Test_0() { var str = "http://user@example.com:8080/abc#a?b=1&c=2"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.Equal(uri.ToString(), str); Assert.Equal(UriScheme.Http, uri.Scheme); @@ -31,7 +31,7 @@ public void Test_0() public void Test_1() { var str = "http://vila/assets/img/vila.svg"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( @@ -50,7 +50,7 @@ public void Test_1() public void Test_2() { var str = "http://localhost"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( @@ -69,7 +69,7 @@ public void Test_2() public void Test_3() { var str = "http://user@example.com:80/abc#a?b=1&c=2"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); Assert.True ( @@ -91,7 +91,7 @@ public void Test_3() public void Test_4() { var str = "http://user@example.com:80/abc#a?b=1&c=2"; - var uri = new UriResource(str); + var uri = new UriEndpoint(str); var segments = new List { new UriPathSegmentRoot(), @@ -107,11 +107,11 @@ public void Test_4() new UriPathSegmentConstant("y") }; - var resourceUri = new UriResource(uri, segments); - resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, extendetSegments); - resourceUri.ServerRoot = new UriResource("http://user@example.com:80"); - resourceUri.ApplicationRoot = new UriResource("http://user@example.com:80"); - resourceUri.EndpointRoot = new UriResource("http://user@example.com:80/abc"); + var resourceUri = new UriEndpoint(uri, segments); + resourceUri = new UriEndpoint(resourceUri, resourceUri.PathSegments, extendetSegments); + resourceUri.ServerRoot = new UriEndpoint("http://user@example.com:80"); + resourceUri.ApplicationRoot = new UriEndpoint("http://user@example.com:80"); + resourceUri.EndpointRoot = new UriEndpoint("http://user@example.com:80/abc"); Assert.True ( diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs new file mode 100644 index 0000000..c124817 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs @@ -0,0 +1,31 @@ +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.Test.Uri +{ + /// + /// Tests the concat method. + /// + [Collection("NonParallelTests")] + public class UnitTestUriConcat + { + /// + /// Test the concat method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", "", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void Concat(string path, string segment, string expected, int count) + { + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var concat = uri.Concat(segment); + + Assert.Equal(expected, concat.ToString()); + Assert.Equal(count, concat.PathSegments.Count()); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs deleted file mode 100644 index e81bef7..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeAppend.cs +++ /dev/null @@ -1,49 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the append method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriRelativeAppend - { - private readonly UriResource Uri = new UriResource("/a/b/c"); - - [Fact] - public void Append_0() - { - var append = Uri.Append("/d"); - - Assert.True - ( - append.ToString() == "/a/b/c/d" && - append.PathSegments.Count == 5 - ); - } - - [Fact] - public void Append_1() - { - var append = Uri.Append("d"); - - Assert.True - ( - append.ToString() == "/a/b/c/d" && - append.PathSegments.Count == 5 - ); - } - - [Fact] - public void Append_2() - { - var append = Uri.Append("/d/e/f"); - - Assert.True - ( - append.ToString() == "/a/b/c/d/e/f" && - append.PathSegments.Count == 7 - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs index 748382f..3e48d03 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs @@ -8,7 +8,7 @@ namespace WebExpress.WebCore.Test.Uri [Collection("NonParallelTests")] public class UnitTestUriRelativeExtendedPath { - private readonly UriResource Uri = new UriResource("http://user@example.com:80"); + private readonly UriEndpoint Uri = new UriEndpoint("http://user@example.com:80"); [Fact] public void ExtendedPath_0() @@ -23,10 +23,10 @@ public void ExtendedPath_0() new UriPathSegmentConstant("y") }; - var resourceUri = new UriResource(Uri, segments); - resourceUri.ServerRoot = new UriResource("http://user@example.com:80"); - resourceUri.ApplicationRoot = new UriResource("http://user@example.com:80"); - resourceUri.EndpointRoot = new UriResource("http://user@example.com:80/a/b/c"); + var resourceUri = new UriEndpoint(Uri, segments); + resourceUri.ServerRoot = new UriEndpoint("http://user@example.com:80"); + resourceUri.ApplicationRoot = new UriEndpoint("http://user@example.com:80"); + resourceUri.EndpointRoot = new UriEndpoint("http://user@example.com:80/a/b/c"); Assert.True ( diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs deleted file mode 100644 index ed040c8..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeSkip.cs +++ /dev/null @@ -1,79 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the skip method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriRelativeSkip - { - private readonly UriResource Uri = new UriResource("/a/b/c"); - - [Fact] - public void Skip_0() - { - var skip = Uri.Skip(0); - - Assert.True - ( - skip.ToString().Equals("/a/b/c") && skip.PathSegments.Count == 4 - ); - } - - [Fact] - public void Skip_1() - { - var skip = Uri.Skip(1); - - Assert.True - ( - skip.ToString().Equals("/a/b/c") && skip.PathSegments.Count == 3 - ); - } - - [Fact] - public void Skip_2() - { - var skip = Uri.Skip(2); - - Assert.True - ( - skip.ToString().Equals("/b/c") && skip.PathSegments.Count == 2 - ); - } - - [Fact] - public void Skip_3() - { - var skip = Uri.Skip(3); - - Assert.True - ( - skip.ToString().Equals("/c") && skip.PathSegments.Count == 1 - ); - } - - [Fact] - public void Skip_4() - { - var skip = Uri.Skip(4); - - Assert.True - ( - skip == null - ); - } - - [Fact] - public void Skip_5() - { - var skip = new UriResource().Skip(1); - - Assert.True - ( - skip == null - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs deleted file mode 100644 index 258f918..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeTake.cs +++ /dev/null @@ -1,134 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the take method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriRelativeTake - { - private readonly UriResource Uri = new UriResource("/a/b/c"); - - [Fact] - public void Take_0() - { - var take = Uri.Take(0); - - Assert.True - ( - take.ToString().Equals("/") && take.PathSegments.Count == 0 - ); - } - - [Fact] - public void Take_1() - { - var take = Uri.Take(1); - - Assert.True - ( - take.ToString().Equals("/") && take.PathSegments.Count == 1 - ); - } - - [Fact] - public void Take_2() - { - var take = Uri.Take(2); - - Assert.True - ( - take.ToString().Equals("/a") && take.PathSegments.Count == 2 - ); - } - - [Fact] - public void Take_3() - { - var take = Uri.Take(3); - - Assert.True - ( - take.ToString().Equals("/a/b") && take.PathSegments.Count == 3 - ); - } - - [Fact] - public void Take_4() - { - var take = Uri.Take(4); - - Assert.True - ( - take.ToString().Equals("/a/b/c") && take.PathSegments.Count == 4 - ); - } - - [Fact] - public void Take_5() - { - var take = Uri.Take(5); - - Assert.True - ( - take.ToString().Equals("/a/b/c") && take.PathSegments.Count == 4 - ); - } - - [Fact] - public void Take_6() - { - var take = Uri.Take(-1); - - Assert.True - ( - take.ToString().Equals("/a/b") && take.PathSegments.Count == 3 - ); - } - - [Fact] - public void Take_7() - { - var take = Uri.Take(-2); - - Assert.True - ( - take.ToString().Equals("/a") && take.PathSegments.Count == 2 - ); - } - - [Fact] - public void Take_8() - { - var take = Uri.Take(-3); - - Assert.True - ( - take.ToString().Equals("/") && take.PathSegments.Count == 1 - ); - } - - [Fact] - public void Take_9() - { - var take = Uri.Take(-4); - - Assert.True - ( - take == null - ); - } - - [Fact] - public void Take_10() - { - var take = Uri.Take(-5); - - Assert.True - ( - take == null - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs new file mode 100644 index 0000000..c836099 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs @@ -0,0 +1,33 @@ +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.Test.Uri +{ + /// + /// Tests the skip method. + /// + [Collection("NonParallelTests")] + public class UnitTestUriSkip + { + /// + /// Test the skip method. + /// + [Theory] + [InlineData("/a/b/c", 0, "/a/b/c", 4)] + [InlineData("/a/b/c", 1, "/a/b/c", 3)] + [InlineData("/a/b/c", 2, "/b/c", 2)] + [InlineData("/a/b/c", 3, "/c", 1)] + [InlineData("/a/b/c", 4, null, 0)] + [InlineData("/a/b/c", 5, null, 0)] + public void Skip(string path, int skipCount, string expected, int count) + { + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var skip = uri.Skip(skipCount); + + Assert.Equal(expected, skip?.ToString()); + Assert.Equal(count, skip?.PathSegments.Count() ?? 0); + } + } +} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs new file mode 100644 index 0000000..a16f038 --- /dev/null +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs @@ -0,0 +1,38 @@ +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.Test.Uri +{ + /// + /// Tests the take method. + /// + [Collection("NonParallelTests")] + public class UnitTestUriTake + { + /// + /// Test the take method. + /// + [Theory] + [InlineData("/a/b/c", 0, "/", 0)] + [InlineData("/a/b/c", 1, "/", 1)] + [InlineData("/a/b/c", 2, "/a", 2)] + [InlineData("/a/b/c", 3, "/a/b", 3)] + [InlineData("/a/b/c", 4, "/a/b/c", 4)] + [InlineData("/a/b/c", 5, "/a/b/c", 4)] + [InlineData("/a/b/c", -1, "/a/b", 3)] + [InlineData("/a/b/c", -2, "/a", 2)] + [InlineData("/a/b/c", -3, "/", 1)] + [InlineData("/a/b/c", -4, null, 0)] + [InlineData("/a/b/c", -5, null, 0)] + public void Take(string path, int takeCount, string expected, int count) + { + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var take = uri.Take(takeCount); + + Assert.Equal(expected, take?.ToString()); + Assert.Equal(count, take?.PathSegments.Count() ?? 0); + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPageB.cs b/src/WebExpress.WebCore.Test/WWW/About.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestPageB.cs rename to src/WebExpress.WebCore.Test/WWW/About.cs index dec02fd..a3326d4 100644 --- a/src/WebExpress.WebCore.Test/TestPageB.cs +++ b/src/WebExpress.WebCore.Test/WWW/About.cs @@ -2,15 +2,13 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebScope; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW { /// /// A dummy class for testing purposes. /// - [Title("webindex:pageb.label")] - [Parent] - [ContextPath("/b")] - public sealed class TestPageB : IPage, IScope + [Title("webindex:about.label")] + public sealed class About : IPage, IScope { /// /// Returns or sets the title of the page. @@ -26,7 +24,7 @@ public sealed class TestPageB : IPage, IScope /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - public TestPageB(IPageContext pageContext) + public About(IPageContext pageContext) { PageContext = pageContext; diff --git a/src/WebExpress.WebCore.Test/TestRestApiA.cs b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs similarity index 95% rename from src/WebExpress.WebCore.Test/TestRestApiA.cs rename to src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs index 5bb64ae..7d2c436 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs @@ -1,13 +1,11 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Api { /// /// A dummy class for testing purposes. /// - [Title("webindex:apia.label")] - [Segment("apia", "webindex:homepage.label")] [Method(CrudMethod.POST)] [Method(CrudMethod.GET)] [Version(1)] diff --git a/src/WebExpress.WebCore.Test/TestRestApiB.cs b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs similarity index 93% rename from src/WebExpress.WebCore.Test/TestRestApiB.cs rename to src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs index 3a3429e..ce98eea 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs @@ -1,14 +1,11 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Api { /// /// A dummy class for testing purposes. /// - [Title("webindex:apib.label")] - [Segment("apib", "webindex:homepage.label")] - [Parent] [Method(CrudMethod.GET)] [Version(2)] public sealed class TestRestApiB : IRestApi diff --git a/src/WebExpress.WebCore.Test/TestRestApiC.cs b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs similarity index 94% rename from src/WebExpress.WebCore.Test/TestRestApiC.cs rename to src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs index 70817e0..b9b6d71 100644 --- a/src/WebExpress.WebCore.Test/TestRestApiC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs @@ -2,14 +2,11 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Api { /// /// A dummy class for testing purposes. /// - [Title("webindex:apic.label")] - [Segment("apic", "webindex:homepage.label")] - [Parent] [Method(CrudMethod.GET)] [Version(3)] public sealed class TestRestApiC : RestApi diff --git a/src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Index.cs similarity index 89% rename from src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs rename to src/WebExpress.WebCore.Test/WWW/Blog/Index.cs index 4d444f5..ae60598 100644 --- a/src/WebExpress.WebCore.Test/Web/Home/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Index.cs @@ -1,19 +1,19 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; -namespace WebExpress.WebCore.Test.Web.Home +namespace WebExpress.WebCore.Test.WWW.Blog { /// /// A dummy class for testing purposes. /// - [Title("webindex:pagec.label")] - public sealed class TestPageH : Page + [Title("webindex:pagee.label")] + public sealed class Index : Page { /// /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - private TestPageH(IPageContext pageContext) + private Index(IPageContext pageContext) { // test the injection if (pageContext == null) diff --git a/src/WebExpress.WebCore.Test/TestPageC.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/Add.cs similarity index 87% rename from src/WebExpress.WebCore.Test/TestPageC.cs rename to src/WebExpress.WebCore.Test/WWW/Blog/Post/Add.cs index d722b11..6f74a0c 100644 --- a/src/WebExpress.WebCore.Test/TestPageC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/Add.cs @@ -1,20 +1,19 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Blog.Post { /// /// A dummy class for testing purposes. /// - [Title("webindex:pagec.label")] - [Segment(null, "webindex:homepage.label")] - public sealed class TestPageC : Page + [Title("webindex:add.label")] + public sealed class Add : Page { /// /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - private TestPageC(IPageContext pageContext) + private Add(IPageContext pageContext) { // test the injection if (pageContext == null) diff --git a/src/WebExpress.WebCore.Test/WWW/Blog/Post/Index.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/Index.cs new file mode 100644 index 0000000..f9f9098 --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/Index.cs @@ -0,0 +1,52 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW.Blog.Post +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:index.label")] + public sealed class Index : Page + { + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + private Index(IPageContext pageContext) + { + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public override void Process(IRenderContext renderContext, TestVisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + + // test the visualTree + if (visualTree == null) + { + throw new ArgumentNullException(nameof(visualTree), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public override void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestPageA.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Edit.cs similarity index 86% rename from src/WebExpress.WebCore.Test/TestPageA.cs rename to src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Edit.cs index 00392d6..67384d7 100644 --- a/src/WebExpress.WebCore.Test/TestPageA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Edit.cs @@ -1,15 +1,13 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId { /// /// A dummy class for testing purposes. /// - [Title("webindex:pagea.label")] - [Segment("pagea", "webindex:homepage.label")] - [ContextPath(null)] - public sealed class TestPageA : IPage + [Title("webindex:edit.label")] + public sealed class Edit : IPage { /// /// Returns or sets the title of the page. @@ -25,7 +23,7 @@ public sealed class TestPageA : IPage /// Initialization of the page. Here, for example, managed resources can be loaded. /// /// The context of the page. - public TestPageA(IPageContext pageContext) + public Edit(IPageContext pageContext) { PageContext = pageContext; diff --git a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs new file mode 100644 index 0000000..a27f087 --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs @@ -0,0 +1,58 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:index.label")] + public sealed class Index : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public Index(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs new file mode 100644 index 0000000..a2adf9f --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs @@ -0,0 +1,10 @@ +using WebExpress.WebCore.WebAttribute; + +namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId +{ + [Name("webindex:segment.label")] + [SegmentGuid("segment")] + public sealed class SegmentInfo + { + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Contact.cs b/src/WebExpress.WebCore.Test/WWW/Contact.cs new file mode 100644 index 0000000..3697d8e --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Contact.cs @@ -0,0 +1,52 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:contact.label")] + public sealed class Contact : Page + { + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + private Contact(IPageContext pageContext) + { + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public override void Process(IRenderContext renderContext, TestVisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + + // test the visualTree + if (visualTree == null) + { + throw new ArgumentNullException(nameof(visualTree), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public override void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Index.cs b/src/WebExpress.WebCore.Test/WWW/Index.cs new file mode 100644 index 0000000..cfc2e40 --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Index.cs @@ -0,0 +1,58 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:home.label")] + public sealed class Index : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public Index(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Products/Details/Index.cs b/src/WebExpress.WebCore.Test/WWW/Products/Details/Index.cs new file mode 100644 index 0000000..edba4ad --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Products/Details/Index.cs @@ -0,0 +1,59 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW.Products.Details +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:index.label")] + [SegmentGuid("")] + public sealed class Index : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public Index(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Products/Index.cs b/src/WebExpress.WebCore.Test/WWW/Products/Index.cs new file mode 100644 index 0000000..4349317 --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Products/Index.cs @@ -0,0 +1,58 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW.Products +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:index.label")] + public sealed class Index : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public Index(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/WWW/Products/List.cs b/src/WebExpress.WebCore.Test/WWW/Products/List.cs new file mode 100644 index 0000000..089f34e --- /dev/null +++ b/src/WebExpress.WebCore.Test/WWW/Products/List.cs @@ -0,0 +1,58 @@ +using WebExpress.WebCore.WebAttribute; +using WebExpress.WebCore.WebPage; + +namespace WebExpress.WebCore.Test.WWW.Products +{ + /// + /// A dummy class for testing purposes. + /// + [Title("webindex:list.label")] + public sealed class List : IPage + { + /// + /// Returns or sets the title of the page. + /// + public string Title { get; set; } + + /// + /// Returns or sets the page context. + /// + public IPageContext PageContext { get; private set; } + + /// + /// Initialization of the page. Here, for example, managed resources can be loaded. + /// + /// The context of the page. + public List(IPageContext pageContext) + { + PageContext = pageContext; + + // test the injection + if (pageContext == null) + { + throw new ArgumentNullException(nameof(pageContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Processing of the page. + /// + /// The context for rendering the page. + /// The visual tree to be rendered. + public void Process(IRenderContext renderContext, VisualTree visualTree) + { + // test the context + if (renderContext == null) + { + throw new ArgumentNullException(nameof(renderContext), "Parameter cannot be null or empty."); + } + } + + /// + /// Release of unmanaged resources reserved during use. + /// + public void Dispose() + { + } + } +} diff --git a/src/WebExpress.WebCore.Test/TestResourceA.cs b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceA.cs similarity index 81% rename from src/WebExpress.WebCore.Test/TestResourceA.cs rename to src/WebExpress.WebCore.Test/WWW/Resources/TestResourceA.cs index b2712e2..4f65bf6 100644 --- a/src/WebExpress.WebCore.Test/TestResourceA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceA.cs @@ -1,15 +1,11 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Resources { /// /// A dummy class for testing purposes. /// - [Title("webindex:resourcea1x.label")] - [Segment("resa", "webindex:homepage.label")] - [ContextPath(null)] public sealed class TestResourceA : IResource { /// @@ -30,7 +26,7 @@ public TestResourceA(IResourceContext resourceContext) /// /// The request. /// The processed response. - public Response Process(WebMessage.Request request) + public Response Process(Request request) { // test the request if (request == null) diff --git a/src/WebExpress.WebCore.Test/TestResourceB.cs b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceB.cs similarity index 79% rename from src/WebExpress.WebCore.Test/TestResourceB.cs rename to src/WebExpress.WebCore.Test/WWW/Resources/TestResourceB.cs index 39c3f96..ab0c2b4 100644 --- a/src/WebExpress.WebCore.Test/TestResourceB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceB.cs @@ -1,14 +1,11 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Resources { /// /// A dummy class for testing purposes. /// - [Segment("resb", "webindex:homepage.label")] - [Parent] public sealed class TestResourceB : IResource { /// @@ -23,7 +20,7 @@ public TestResourceB() /// /// The request. /// The processed response. - public Response Process(WebMessage.Request request) + public Response Process(Request request) { // test the request if (request == null) diff --git a/src/WebExpress.WebCore.Test/TestResourceC.cs b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceC.cs similarity index 84% rename from src/WebExpress.WebCore.Test/TestResourceC.cs rename to src/WebExpress.WebCore.Test/WWW/Resources/TestResourceC.cs index 6521a77..538e866 100644 --- a/src/WebExpress.WebCore.Test/TestResourceC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceC.cs @@ -1,15 +1,11 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Resources { /// /// A dummy class for testing purposes. /// - [Title("webindex:resourcea2x.label")] - [Segment("resc", "webindex:homepage.label")] - [ContextPath(null)] public sealed class TestResourceC : IResource { /// @@ -37,7 +33,7 @@ public TestResourceC(IResourceManager resourceManager, IResourceContext resource /// /// The request. /// The processed response. - public Response Process(WebMessage.Request request) + public Response Process(Request request) { // test the request if (request == null) diff --git a/src/WebExpress.WebCore.Test/TestResourceD.cs b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceD.cs similarity index 85% rename from src/WebExpress.WebCore.Test/TestResourceD.cs rename to src/WebExpress.WebCore.Test/WWW/Resources/TestResourceD.cs index 0684e15..7cc397c 100644 --- a/src/WebExpress.WebCore.Test/TestResourceD.cs +++ b/src/WebExpress.WebCore.Test/WWW/Resources/TestResourceD.cs @@ -1,14 +1,11 @@ -using WebExpress.WebCore.WebAttribute; -using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebResource; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Resources { /// /// A dummy class for testing purposes. /// - [Title("webindex:resourceab1x.label")] - [Segment("resd", "webindex:homepage.label")] public sealed class TestResourceD : IResource { /// @@ -36,7 +33,7 @@ public TestResourceD(IResourceContext resourceContext, IResourceManager resource /// /// The request. /// The processed response. - public Response Process(WebMessage.Request request) + public Response Process(Request request) { // test the request if (request == null) diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryA.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestSettingCategoryA.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryA.cs index b27f5be..79db23e 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryA.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting category for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryB.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestSettingCategoryB.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryB.cs index 27f9500..bd6e566 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryB.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting category for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryC.cs similarity index 89% rename from src/WebExpress.WebCore.Test/TestSettingCategoryC.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryC.cs index 88e13e8..725c66c 100644 --- a/src/WebExpress.WebCore.Test/TestSettingCategoryC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingCategoryC.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting category for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupA.cs similarity index 90% rename from src/WebExpress.WebCore.Test/TestSettingGroupA.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupA.cs index eabad40..81a99df 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupA.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting group for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupB.cs similarity index 90% rename from src/WebExpress.WebCore.Test/TestSettingGroupB.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupB.cs index 320271e..05f815a 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupB.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting group for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupC.cs similarity index 88% rename from src/WebExpress.WebCore.Test/TestSettingGroupC.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupC.cs index 4c4f0da..8bb3bf2 100644 --- a/src/WebExpress.WebCore.Test/TestSettingGroupC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingGroupC.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy setting group for testing purposes. diff --git a/src/WebExpress.WebCore.Test/TestSettingPageA.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageA.cs similarity index 93% rename from src/WebExpress.WebCore.Test/TestSettingPageA.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageA.cs index 86da58f..81340d0 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageA.cs @@ -2,15 +2,13 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy class for testing purposes. /// [WebIcon] [Title("webindex:settingpagea.label")] - [Segment("settingpagea", "webindex:homepage.label")] - [ContextPath(null)] [SettingGroup()] public sealed class TestSettingPageA : ISettingPage { diff --git a/src/WebExpress.WebCore.Test/TestSettingPageB.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageB.cs similarity index 93% rename from src/WebExpress.WebCore.Test/TestSettingPageB.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageB.cs index 73733ae..4530e2a 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageB.cs @@ -2,15 +2,13 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy class for testing purposes. /// [WebIcon] [Title("webindex:settingpageb.label")] - [Segment("settingpagea", "webindex:homepage.label")] - [ContextPath(null)] [SettingGroup] public sealed class TestSettingPageB : ISettingPage { diff --git a/src/WebExpress.WebCore.Test/TestSettingPageC.cs b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageC.cs similarity index 93% rename from src/WebExpress.WebCore.Test/TestSettingPageC.cs rename to src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageC.cs index d085c33..3f29a23 100644 --- a/src/WebExpress.WebCore.Test/TestSettingPageC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Settings/TestSettingPageC.cs @@ -2,14 +2,12 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebSettingPage; -namespace WebExpress.WebCore.Test +namespace WebExpress.WebCore.Test.WWW.Settings { /// /// A dummy class for testing purposes. /// [Title("webindex:settingpageb.label")] - [Segment("settingpagea", "webindex:homepage.label")] - [ContextPath(null)] public sealed class TestSettingPageC : ISettingPage { /// diff --git a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj index 5c66e53..aa17a51 100644 --- a/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj +++ b/src/WebExpress.WebCore.Test/WebExpress.WebCore.Test.csproj @@ -54,7 +54,7 @@ - + diff --git a/src/WebExpress.WebCore/Config/HttpServerConfig.cs b/src/WebExpress.WebCore/Config/HttpServerConfig.cs index 18cd34e..7a80762 100644 --- a/src/WebExpress.WebCore/Config/HttpServerConfig.cs +++ b/src/WebExpress.WebCore/Config/HttpServerConfig.cs @@ -23,10 +23,10 @@ public sealed class HttpServerConfig public List Endpoints { get; set; } /// - /// The uri of the web server. + /// The route of the web server. /// - [XmlElement("uri")] - public string Uri { get; set; } + [XmlElement("route")] + public string Route { get; set; } /// /// The limitations. diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 04968cd..706f47d 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -79,7 +79,7 @@ public HttpServer(HttpServerContext context) { HttpServerContext = new HttpServerContext ( - context.Uri, + context.Route, context.Endpoints, context.PackagePath, context.AssetPath, @@ -263,12 +263,12 @@ private Response HandleClient(HttpContext context) if (searchResult != null) { - var resourceUri = new UriResource(request.Uri, searchResult.Uri.PathSegments); - resourceUri = new UriResource(resourceUri, resourceUri.PathSegments, request.Uri.Skip(resourceUri.PathSegments.Count)?.PathSegments) + var resourceUri = new UriEndpoint(request.Uri, searchResult.Uri.PathSegments); + resourceUri = new UriEndpoint((IUri)resourceUri) { - ServerRoot = new UriResource(request.Uri, HttpServerContext.ContextPath.PathSegments), - ApplicationRoot = new UriResource(request.Uri, searchResult.EndpointContext?.ApplicationContext?.ContextPath.PathSegments), - EndpointRoot = new UriResource(request.Uri, searchResult.Uri.PathSegments) + ServerRoot = new UriEndpoint(request.Uri, HttpServerContext.ContextPath.PathSegments), + ApplicationRoot = new UriEndpoint(request.Uri, searchResult.EndpointContext?.ApplicationContext?.ContextPath.PathSegments), + EndpointRoot = new UriEndpoint(request.Uri, searchResult.Uri.PathSegments) }; request.Uri = resourceUri; diff --git a/src/WebExpress.WebCore/HttpServerContext.cs b/src/WebExpress.WebCore/HttpServerContext.cs index 708ab1e..76db1ee 100644 --- a/src/WebExpress.WebCore/HttpServerContext.cs +++ b/src/WebExpress.WebCore/HttpServerContext.cs @@ -2,8 +2,8 @@ using System.Globalization; using System.Reflection; using WebExpress.WebCore.Config; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore { @@ -13,9 +13,9 @@ namespace WebExpress.WebCore public class HttpServerContext : IHttpServerContext { /// - /// Returns the uri of the web server. + /// Returns the route of the web server. /// - public string Uri { get; protected set; } + public IRoute Route { get; protected set; } /// /// Returns the endpoints to which the web server responds. @@ -50,7 +50,7 @@ public class HttpServerContext : IHttpServerContext /// /// Returns the basic context path. /// - public UriResource ContextPath { get; protected set; } + public IRoute ContextPath { get; protected set; } /// /// Returns the culture. @@ -70,7 +70,7 @@ public class HttpServerContext : IHttpServerContext /// /// Initializes a new instance of the class. /// - /// The uri of the web server. + /// The uri of the route server. /// The endpoints to which the web server responds. /// The package home directory.chnis /// The asset home directory. @@ -82,13 +82,13 @@ public class HttpServerContext : IHttpServerContext /// The host. public HttpServerContext ( - string uri, + IRoute route, ICollection endpoints, string packageBaseFolder, string assetBaseFolder, string dataBaseFolder, string configBaseFolder, - UriResource contextPath, + IRoute contextPath, CultureInfo culture, ILog log, IHost host @@ -97,7 +97,7 @@ IHost host var assembly = typeof(HttpServer).Assembly; Version = assembly.GetCustomAttribute()?.InformationalVersion; - Uri = uri; + Route = route; Endpoints = endpoints; PackagePath = packageBaseFolder; AssetPath = assetBaseFolder; diff --git a/src/WebExpress.WebCore/IHttpServerContext.cs b/src/WebExpress.WebCore/IHttpServerContext.cs index de99e2c..1ef850b 100644 --- a/src/WebExpress.WebCore/IHttpServerContext.cs +++ b/src/WebExpress.WebCore/IHttpServerContext.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Globalization; using WebExpress.WebCore.Config; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore { @@ -12,9 +12,9 @@ namespace WebExpress.WebCore public interface IHttpServerContext { /// - /// Returns the uri of the web server. + /// Returns the route of the web server. /// - string Uri { get; } + IRoute Route { get; } /// /// Returns the endpoints to which the web server responds. @@ -49,7 +49,7 @@ public interface IHttpServerContext /// /// Returns the basic context path. /// - UriResource ContextPath { get; } + IRoute ContextPath { get; } /// /// Returns the culture. diff --git a/src/WebExpress.WebCore/Internationalization/de b/src/WebExpress.WebCore/Internationalization/de index 53b04a6..8d5479e 100644 --- a/src/WebExpress.WebCore/Internationalization/de +++ b/src/WebExpress.WebCore/Internationalization/de @@ -98,13 +98,11 @@ assetmanager.initialization=Der Assetmanager wurde initialisiert. assetmanager.addresource=Das Asset '{0}' wurde in der Anwendung '{1}' registiert. pagemanager.initialization=Der Pagemanager wurde initialisiert. -pagemanager.addresource=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. -pagemanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. -pagemanager.resource=Seite: '{0}' für die Anwendung '{1}' +pagemanager.addpage=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. +pagemanager.page=Seite: '{0}' für die Anwendung '{1}' restapimanager.initialization=Der RestApiManager wurde initialisiert. -restapimanager.addresource=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. -restapimanager.addresource.duplicate=Die Seite '{0}' der Anwendung '{1}' ist bereits hinzugefügt worden. +restapimanager.addrestapi=Die Seite '{0}' wurde in der Anwendung '{1}' registiert. restapimanager.resource=Seite: '{0}' für die Anwendung '{1}' restapimanager.methodnotsupported=Die Methode '{0}' wird nicht unterstützt. diff --git a/src/WebExpress.WebCore/Internationalization/en b/src/WebExpress.WebCore/Internationalization/en index 8564eaf..c2dcfb9 100644 --- a/src/WebExpress.WebCore/Internationalization/en +++ b/src/WebExpress.WebCore/Internationalization/en @@ -98,13 +98,11 @@ assetmanager.initialization=The asset manager has been initialized. assetmanager.addresource=The asset '{0}' has been registered in the application '{1}'. pagemanager.initialization=The page manager has been initialized. -pagemanager.addresource=The page '{0}' has been registered in the application '{1}'. -pagemanager.addresource.duplicate=The page '{0}' of application '{1}' has already been added. -pagemanager.resource=Page: '{0}' for application '{1}' +pagemanager.addpage=The page '{0}' has been registered in the application '{1}'. +pagemanager.page=Page: '{0}' for application '{1}' restapimanager.initialization=The REST API manager has been initialized. -restapimanager.addresource=The REST API '{0}' has been registered in the application '{1}'. -restapimanager.addresource.duplicate=The REST API '{0}' of application '{1}' has already been added. +restapimanager.addrestapi=The REST API '{0}' has been registered in the application '{1}'. restapimanager.resource=REST API: '{0}' for application '{1}' restapimanager.methodnotsupported=The method '{0}' is not supported. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs index 267dbf2..14c97f1 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationContext.cs @@ -1,5 +1,5 @@ -using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; namespace WebExpress.WebCore.WebApplication { @@ -41,12 +41,12 @@ public class ApplicationContext : IApplicationContext /// /// Returns the context path. This is mounted in the context path of the server. /// - public UriResource ContextPath { get; internal set; } + public IRoute ContextPath { get; internal set; } /// /// Returns the icon uri. /// - public UriResource Icon { get; internal set; } + public IRoute Icon { get; internal set; } /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs index c53905c..139ceb7 100644 --- a/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs +++ b/src/WebExpress.WebCore/WebApplication/ApplicationManager.cs @@ -9,9 +9,9 @@ using WebExpress.WebCore.WebApplication.Model; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebApplication { @@ -129,8 +129,8 @@ private void Register(IPluginContext pluginContext) Description = description, AssetPath = Path.Combine(_httpServerContext.AssetPath, assetPath), DataPath = Path.Combine(_httpServerContext.DataPath, dataPath), - Icon = UriResource.Combine(_httpServerContext.ContextPath, contextPath, icon), - ContextPath = UriResource.Combine(_httpServerContext.ContextPath, contextPath) + Icon = RouteEndpoint.Combine(_httpServerContext.ContextPath, contextPath, icon), + ContextPath = RouteEndpoint.Combine(_httpServerContext.ContextPath, contextPath) }; // create application diff --git a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs index 4cddfc5..23c2062 100644 --- a/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs +++ b/src/WebExpress.WebCore/WebApplication/IApplicationContext.cs @@ -1,6 +1,6 @@ using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebApplication { @@ -42,11 +42,11 @@ public interface IApplicationContext : IContext /// /// Returns the context path. This is mounted in the context path of the server. /// - UriResource ContextPath { get; } + IRoute ContextPath { get; } /// /// Returns the icon uri. /// - UriResource Icon { get; } + IRoute Icon { get; } } } diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs index 7961173..7be77fd 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetContext.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -13,7 +14,7 @@ namespace WebExpress.WebCore.WebAsset /// public class AssetContext : IAssetContext { - private readonly UriResource _contextPath; + private readonly IRoute _contextPath; private readonly IUriPathSegment _pathSegment; /// @@ -36,11 +37,6 @@ public class AssetContext : IAssetContext /// public IComponentId EndpointId { get; internal set; } - /// - /// Returns the parent or null if not used. - /// - public IEndpointContext ParentContext => null; - /// /// Returns whether the resource is created once and reused each time it is called. /// @@ -52,21 +48,21 @@ public class AssetContext : IAssetContext public bool IncludeSubPaths { get; internal set; } /// - /// Returns the context path. + /// Returns the internal routing path for the endpoint. /// - public UriResource ContextPath => UriResource.Combine(ApplicationContext.ContextPath, _contextPath); + public IRoute Route => RouteEndpoint.Combine(ApplicationContext.ContextPath, _contextPath, _pathSegment); /// - /// Returns the uri. + /// Returns the attributes associated with the page. /// - public UriResource Uri => ContextPath.Append(_pathSegment); + public IEnumerable Attributes => []; /// /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. /// /// The context path of the resource. /// The path segment of the resource. - public AssetContext(UriResource contextPath, IUriPathSegment pathSegment) + public AssetContext(IRoute contextPath, IUriPathSegment pathSegment) { _contextPath = contextPath; _pathSegment = pathSegment; diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index bb38b28..ada9019 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -142,7 +142,7 @@ private void Register(IPluginContext pluginContext, IEnumerable - /// Attribute to specify the parent endpoint for a given endpoint. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ParentAttribute : Attribute, IEndpointAttribute - where TEndpoint : class, IEndpoint - { - /// - /// Initializes a new instance of the class. - /// - public ParentAttribute() - { - - } - } -} diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 2d6d95f..f5d3393 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -2,10 +2,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebMessage; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebEndpoint { @@ -14,7 +18,9 @@ namespace WebExpress.WebCore.WebEndpoint /// public sealed class EndpointManager : IEndpointManager, ISystemComponent { - //private readonly IComponentHub _componentHub; + private static readonly string[] _namespacePrefixes = ["page", "pages", "webpage", "webpages", "website", "www", "web"]; + private static readonly string _indexPrefix = "index"; + private static readonly string[] _classSuffixes = ["page"]; private readonly IHttpServerContext _httpServerContext; private readonly Dictionary _registrations = []; @@ -139,5 +145,107 @@ private void OnRemoveEndpoint(object sender, IEndpointContext endpointContext) public void Dispose() { } + + /// + /// Returns the route of an endpoint based on the class type, application context and segment attributes. + /// + /// The type of the class. + /// The application context. + /// The segment attribute. + /// The intermediate segments. + /// The namespace prefixes. + /// The route of the endpoint. + public static IRoute CreateEndpointRoute + ( + Type classType, + IApplicationContext applicationContext, + ISegmentAttribute segment, + IEnumerable intermediateSegments = null, + string[] namespacePrefixes = null + ) + { + var assemblyName = classType.Assembly.GetName().Name; + var fullClassName = classType.FullName; + var className = _classSuffixes?.FirstOrDefault(s => classType.Name.ToLowerInvariant().EndsWith(s, StringComparison.OrdinalIgnoreCase)) is string suffix + ? classType.Name.ToLowerInvariant()[..^suffix.Length] + : classType.Name.ToLowerInvariant(); + var segments = (fullClassName.Length - className.Length - 1 > assemblyName.Length) + ? fullClassName[(assemblyName.Length + 1)..^(classType.Name.Length + 1)].ToLowerInvariant().Split('.', StringSplitOptions.RemoveEmptyEntries) + : []; + + var segmentAttributesMapping = segments.Select((segment, index) => new + { + FullNamespace = $"{assemblyName}.{string.Join(".", segments.Take(index + 1))}", + Segment = segment + }).Select(s => + { + var segmentResult = default(IUriPathSegment); + var name = default(string); + var description = default(string); + var icon = default(IIcon); + + var typeName = $"{s.FullNamespace}.SegmentInfo"; + var segmentInfoType = classType.Assembly.GetType(typeName, throwOnError: false, ignoreCase: true); + + if (segmentInfoType != null) + { + var segAttrType = segmentInfoType.CustomAttributes + .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) + .Select(x => x.AttributeType) + .FirstOrDefault(); + + var segInstance = segAttrType != null + ? segmentInfoType.GetCustomAttribute(segAttrType, false) as ISegmentAttribute + : null; + var nameAttr = segmentInfoType.CustomAttributes + .FirstOrDefault(x => x.AttributeType == typeof(NameAttribute)); + var descAttr = segmentInfoType.CustomAttributes + .FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute)); + var iconAttr = segmentInfoType.CustomAttributes + .FirstOrDefault(x => x.AttributeType.IsGenericType && + x.AttributeType.GetGenericTypeDefinition() == typeof(WebIconAttribute<>)); + + segmentResult = segInstance?.ToPathSegment(); + name = nameAttr?.ConstructorArguments.FirstOrDefault().Value?.ToString(); + description = descAttr?.ConstructorArguments.FirstOrDefault().Value?.ToString(); + icon = iconAttr != null + ? Activator.CreateInstance(iconAttr.AttributeType.GenericTypeArguments.FirstOrDefault()) as IIcon + : null; + } + + return new + { + Segment = segmentResult ?? new UriPathSegmentConstant(s.Segment), + Name = name, + Description = description, + Icon = icon + }; + }); + + segmentAttributesMapping = segmentAttributesMapping.Any() && _namespacePrefixes + .Contains(segmentAttributesMapping + .First().Segment + .ToString()) + ? segmentAttributesMapping.Skip(1) + : segmentAttributesMapping; + + segmentAttributesMapping = segmentAttributesMapping.Any() && (namespacePrefixes ?? []) + .Contains(segmentAttributesMapping + .First().Segment + .ToString()) + ? segmentAttributesMapping.Skip(1) + : segmentAttributesMapping; + + var uri = RouteEndpoint.Combine + ( + applicationContext.ContextPath, + (intermediateSegments ?? []) + .Concat(segmentAttributesMapping + .Select(x => x.Segment)) + ) + .Concat(segment?.ToPathSegment() ?? new UriPathSegmentConstant(!className.StartsWith(_indexPrefix) ? className : null)); + + return uri; + } } } diff --git a/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs index 6ba82c8..0a37b8f 100644 --- a/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IEndpointContext.cs @@ -1,9 +1,9 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebEndpoint { @@ -32,11 +32,6 @@ public interface IEndpointContext : IContext /// IEnumerable Conditions { get; } - /// - /// Returns the parent or null if not used. - /// - IEndpointContext ParentContext { get; } - /// /// Determines whether the resource is created once and reused each time it is called. /// @@ -48,13 +43,13 @@ public interface IEndpointContext : IContext bool IncludeSubPaths { get; } /// - /// Returns the context path. + /// Returns the internal routing path for the endpoint. /// - UriResource ContextPath { get; } + IRoute Route { get; } /// - /// Returns the uri. + /// Returns the attributes associated with the page. /// - UriResource Uri { get; } + IEnumerable Attributes { get; } } } diff --git a/src/WebExpress.WebCore/WebEndpoint/IRoute.cs b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs new file mode 100644 index 0000000..7f0f560 --- /dev/null +++ b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebEndpoint +{ + /// + /// A route in WebExpress represents a logical internal path or pattern used to map endpoints within the sitemap. + /// + /// While a URI (Uniform Resource Identifier) defines the complete, standardized external identifier of a resource + /// (e.g., http://example.com/path?query=value#fragment) and thus represents the external address of an endpoint, + /// a route represents the internal address of an endpoint that is used exclusively for internal purposes such as routing + /// and request processing. + /// + /// A route contains neither a scheme, nor an authority, nor other external components. Furthermore, it may include + /// placeholders for dynamic segments (e.g., /users/{id}). + /// + public interface IRoute + { + /// + /// The segments of the route (e.g., /over/there). + /// + IEnumerable PathSegments { get; } + + /// + /// Returns a string representation of the route. + /// + string Display { get; } + + /// + /// Determines if the route is the root. + /// + bool IsRoot { get; } + + /// + /// Concatenates the given path segment to the current route and returns a new instance of IRoute with the updated path. + /// + /// The path segment to be concatenated with the existing route. + /// A new IRoute instance representing the route after concatenation. + IRoute Concat(string segment); + + /// + /// Concatenates the given path segment to the current route and returns a new instance of IRoute with the updated path. + /// + /// An array of path segments to be concatenated to the existing route. + /// A new IRoute instance representing the route after concatenation. + IRoute Concat(params IUriPathSegment[] segments); + } +} diff --git a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs new file mode 100644 index 0000000..bff8ba0 --- /dev/null +++ b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using WebExpress.WebCore.WebUri; + +namespace WebExpress.WebCore.WebEndpoint +{ + /// + /// A route in WebExpress represents a logical internal path or pattern used to map endpoints within the sitemap. + /// + /// While a URI (Uniform Resource Identifier) defines the complete, standardized external identifier of a resource + /// (e.g., http://example.com/path?query=value#fragment) and thus represents the external address of an endpoint, + /// a route represents the internal address of an endpoint that is used exclusively for internal purposes such as routing + /// and request processing. + /// + /// A route contains neither a scheme, nor an authority, nor other external components. Furthermore, it may include + /// placeholders for dynamic segments (e.g., /users/{id}). + /// + public partial class RouteEndpoint : IRoute + { + /// + /// The segments of the route (e.g., /over/there). + /// + public IEnumerable PathSegments { get; private set; } = [new UriPathSegmentRoot()]; + + /// + /// Returns a string representation of the route. + /// + public string Display { get; set; } + + /// + /// Determines if the route is the root. + /// + public bool IsRoot => PathSegments.Count() == 1; + + /// + /// Initializes a new instance of the class. + /// + public RouteEndpoint() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The scheme (e.g. Http, FTP). + /// The authority (e.g. user@example.com:8080). + /// The uri. + public RouteEndpoint(UriScheme scheme, UriAuthority authority, string uri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The route. + public RouteEndpoint(string route) + { + if (string.IsNullOrWhiteSpace(route) || route == "/") return; + + PathSegments = PathSegments + .Concat(route.Split('/', StringSplitOptions.RemoveEmptyEntries) + .Select(x => new UriPathSegmentConstant(x))); + } + + /// + /// Copy constructor + /// + /// The route. + public RouteEndpoint(IRoute route) + { + PathSegments = route?.PathSegments.Select(x => x.Copy()) ?? []; + } + + /// + /// Initializes a new instance of the class. + /// + /// The path segments. + public RouteEndpoint(params IUriPathSegment[] segments) + { + if (segments.Length > 0) + { + PathSegments = PathSegments + .Concat(segments.Select(x => x.Copy())); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The base route. + /// The path segments. + public RouteEndpoint(IRoute route, params string[] segments) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The base route. + /// The path segments. + public RouteEndpoint(IRoute route, params IUriPathSegment[] segments) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The route. + /// The path segments. + /// Other segments. + public RouteEndpoint(IRoute route, IEnumerable segments, IEnumerable extendedSegments) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The scheme (e.g. Http, FTP). + /// The authority (e.g. user@example.com:8080). + /// References a position within a resource (e.g. #Anchor). + /// The query part (e.g. ?title=Uniform_Resource_Identifier). + /// The path segments. + public RouteEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) + { + } + + /// + /// Concatenates the given path segment to the current route and returns a new instance of IRoute with the updated path. + /// + /// The path segment to be concatenated with the existing route. + /// A new IRoute instance representing the route after concatenation. + public IRoute Concat(string segment) + { + if (string.IsNullOrWhiteSpace(segment)) + { + return this; + } + + var copy = new RouteEndpoint((IRoute)this); + copy.PathSegments = copy.PathSegments + .Concat(segment.Split('/', StringSplitOptions.RemoveEmptyEntries) + .Select(x => new UriPathSegmentConstant(x))); + + return copy; + } + + /// + /// Concatenates the given path segment to the current route and returns a new instance of IRoute with the updated path. + /// + /// An array of path segments to be concatenated to the existing route. + /// A new IRoute instance representing the route after concatenation. + public virtual IRoute Concat(params IUriPathSegment[] segments) + { + if (segments.Length == 0) + { + return this; + } + + var copy = new RouteEndpoint((IRoute)this); + copy.PathSegments = copy.PathSegments + .Select(x => x.Copy()) + .Concat(segments.Where(x => !x.IsEmpty)); + + return copy; + } + + /// + /// Combines the specified routes into a compound route. + /// + /// The routes to be combine. + /// A combined route. + public static IRoute Combine(params IRoute[] routes) + { + var r = routes.Skip(1).Where(x => !x.IsRoot).SelectMany(x => x.PathSegments.Skip(1)); + var copy = new RouteEndpoint(routes.FirstOrDefault()); + copy.PathSegments = copy.PathSegments + .Concat(r); + + return copy; + } + + /// + /// Combines the specified routes into a compound uri. + /// + /// The base route to be used as the starting point. + /// The routes to be combine. + /// A combined route. + public static IRoute Combine(IRoute baseRoute, params string[] routes) + { + var copy = new RouteEndpoint(baseRoute); + copy.PathSegments = copy.PathSegments + .Concat(routes.Where(x => !string.IsNullOrWhiteSpace(x)) + .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) + .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); + + return copy; + } + + /// + /// Combines the specified base route, intermediate route, and enumerable segments into a compound route. + /// + /// The base route serving as the starting point. + /// The enumerable collection of path segments to be added. + /// A new compound route combining all specified components. + public static IRoute Combine(IRoute baseRoute, IEnumerable segments) + { + var copy = new RouteEndpoint(baseRoute); + copy.PathSegments = copy.PathSegments + .Concat(segments); + + return copy; + } + + /// + /// Combines the specified base route, intermediate route, and enumerable segments into a compound route. + /// + /// The base route serving as the starting point. + /// The intermediate route to be appended, if not empty or whitespace. + /// The enumerable collection of path segments to be added. + /// A new compound route combining all specified components. + public static IRoute Combine(IRoute baseRoute, string intermediateRoute, IEnumerable segments) + { + var copy = new RouteEndpoint(baseRoute); + copy.PathSegments = copy.PathSegments + .Concat(!string.IsNullOrWhiteSpace(intermediateRoute) ? [new UriPathSegmentConstant(intermediateRoute)] : []) + .Concat(segments); + + return copy; + } + + /// + /// Combines the specified base route, intermediate route, and single segment into a compound route. + /// + /// The base route to be used as the starting point. + /// The intermediate route to be appended. + /// The individual segment to be added to the resulting route. + /// A newly combined route constructed from the provided components. + public static IRoute Combine(IRoute baseRoute, IRoute intermediateRoute, IUriPathSegment segment) + { + return Combine([baseRoute, intermediateRoute, new RouteEndpoint(segment)]); + } + + /// + /// Converts a resource uri to a normal uri. + /// + /// The uri to convert. + public static implicit operator string(RouteEndpoint uri) + { + return uri?.ToString(); + } + + /// + /// Converts the route to a string. + /// + /// A string that represents the current route. + public override string ToString() + { + var path = "/" + string.Join + ( + "/", + PathSegments.Where(x => x is not UriPathSegmentRoot) + .Select(x => x.ToString().TrimStart('/')) + ); + + return path?.TrimEnd('/'); + } + } +} \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebEx.cs b/src/WebExpress.WebCore/WebEx.cs index 572d9a7..5a7f82c 100644 --- a/src/WebExpress.WebCore/WebEx.cs +++ b/src/WebExpress.WebCore/WebEx.cs @@ -8,9 +8,9 @@ using WebExpress.WebCore.Config; using WebExpress.WebCore.Internationalization; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPackage; -using WebExpress.WebCore.WebUri; [assembly: InternalsVisibleTo("WebExpress.WebCore.Test")] @@ -183,13 +183,13 @@ private void Initialization(string args, string configFile) var context = new HttpServerContext ( - config.Uri, + new RouteEndpoint(config.Route), config.Endpoints, Path.GetFullPath(packageBase), Path.GetFullPath(assetBase), Path.GetFullPath(dataBase), Path.GetDirectoryName(configFile), - new UriResource(config.ContextPath), + new RouteEndpoint(config.ContextPath), culture, log, null diff --git a/src/WebExpress.WebCore/WebMessage/Parameter.cs b/src/WebExpress.WebCore/WebMessage/Parameter.cs index 17c7a75..8f1d4fd 100644 --- a/src/WebExpress.WebCore/WebMessage/Parameter.cs +++ b/src/WebExpress.WebCore/WebMessage/Parameter.cs @@ -102,17 +102,17 @@ public Parameter(string key, char value, ParameterScope scope) /// The parameter list. public static List Create(params Parameter[] param) { - return new List(param); + return [.. param]; } /// /// Returns the key. /// - /// The type. + /// The type. /// The key. - public static string GetKey() where T : Parameter + public static string GetKey() where TParameter : Parameter { - return (Activator.CreateInstance(typeof(T)) as T)?.Key; + return Activator.CreateInstance()?.Key; } /// diff --git a/src/WebExpress.WebCore/WebMessage/Request.cs b/src/WebExpress.WebCore/WebMessage/Request.cs index 3f9333e..f0a706d 100644 --- a/src/WebExpress.WebCore/WebMessage/Request.cs +++ b/src/WebExpress.WebCore/WebMessage/Request.cs @@ -33,7 +33,7 @@ public class Request /// /// Returns the uri. /// - public UriResource Uri { get; internal set; } + public UriEndpoint Uri { get; internal set; } /// /// Returns the parameters. @@ -151,7 +151,7 @@ internal Request(IFeatureCollection contextFeatures, RequestHeaderFields header, LocalEndPoint = new IPEndPoint(connectionFeature.LocalIpAddress, connectionFeature.LocalPort); RemoteEndPoint = new IPEndPoint(connectionFeature.RemoteIpAddress, connectionFeature.RemotePort); - Uri = new UriResource + Uri = new UriEndpoint ( Scheme, new UriAuthority() diff --git a/src/WebExpress.WebCore/WebPage/IPageContext.cs b/src/WebExpress.WebCore/WebPage/IPageContext.cs index 99a9f1b..12e91b0 100644 --- a/src/WebExpress.WebCore/WebPage/IPageContext.cs +++ b/src/WebExpress.WebCore/WebPage/IPageContext.cs @@ -20,10 +20,5 @@ public interface IPageContext : IEndpointContext /// determine whether content and how content should be displayed. /// IEnumerable Scopes { get; } - - /// - /// Returns the attributes associated with the page. - /// - IEnumerable Attributes { get; } } } diff --git a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs index 3532e3e..87f0abc 100644 --- a/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/IVisualTreeContext.cs @@ -16,7 +16,7 @@ public interface IVisualTreeContext /// /// The uri of the request. /// - UriResource Uri { get; } + UriEndpoint Uri { get; } /// /// Return or sets the render context. diff --git a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs index 0b5d3fa..9f27f7e 100644 --- a/src/WebExpress.WebCore/WebPage/Model/PageItem.cs +++ b/src/WebExpress.WebCore/WebPage/Model/PageItem.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage.Model { @@ -15,9 +13,6 @@ namespace WebExpress.WebCore.WebPage.Model /// internal class PageItem : IDisposable { - private readonly IEndpointManager _endpointManager; - private PageContext _pageContext; - /// /// Returns the endpoint id. /// @@ -38,11 +33,6 @@ internal class PageItem : IDisposable /// public string Title { get; set; } - /// - /// Returns or sets the parent type. - /// - public Type ParentType { get; set; } - /// /// Returns or sets the type of page. /// @@ -60,16 +50,6 @@ internal class PageItem : IDisposable /// public IEnumerable Scopes { get; set; } - /// - /// Returns or sets the paths of the resource. - /// - public UriResource ContextPath { get; set; } - - /// - /// Returns or sets the path segment. - /// - public IUriPathSegment PathSegment { get; internal set; } - /// /// Returns or sets whether all subpaths should be taken into sitemap. /// @@ -98,38 +78,7 @@ internal class PageItem : IDisposable /// /// Returns the page context. /// - public IPageContext PageContext - { - get - { - _pageContext ??= new PageContext() - { - PageTitle = Title, - EndpointId = EndpointId, - PluginContext = PluginContext, - ApplicationContext = ApplicationContext, - Cache = Cache, - Scopes = Scopes, - Conditions = Conditions, - IncludeSubPaths = IncludeSubPaths, - Attributes = Attributes, - }; - - var parentContext = _endpointManager.GetEndpoints(ParentType, ApplicationContext) - .FirstOrDefault(); - - var contextPath = UriResource.Combine - ( - parentContext?.Uri ?? ApplicationContext?.ContextPath, ContextPath - ); - - _pageContext.ParentContext = parentContext; - _pageContext.ContextPath = contextPath; - _pageContext.Uri = contextPath.Append(PathSegment); - - return _pageContext; - } - } + public IPageContext PageContext { get; internal set; } /// /// Initializes a new instance of the class. @@ -137,7 +86,6 @@ public IPageContext PageContext /// The endpoint manager responsible for managing endpoints. internal PageItem(IEndpointManager endpointManager) { - _endpointManager = endpointManager; } /// @@ -145,7 +93,6 @@ internal PageItem(IEndpointManager endpointManager) /// public void Dispose() { - } /// diff --git a/src/WebExpress.WebCore/WebPage/PageContext.cs b/src/WebExpress.WebCore/WebPage/PageContext.cs index 7312062..a350cd3 100644 --- a/src/WebExpress.WebCore/WebPage/PageContext.cs +++ b/src/WebExpress.WebCore/WebPage/PageContext.cs @@ -46,11 +46,6 @@ public class PageContext : IPageContext /// public string PageTitle { get; internal set; } - /// - /// Returns the parent or null if not used. - /// - public IEndpointContext ParentContext { get; internal set; } - /// /// Returns whether the resource is created once and reused each time it is called. /// @@ -69,12 +64,12 @@ public class PageContext : IPageContext /// /// Returns the context path. /// - public UriResource ContextPath { get; internal set; } + public UriEndpoint ContextPath { get; internal set; } /// - /// Returns the uri. + /// Returns the internal routing path for the endpoint. /// - public UriResource Uri { get; internal set; } + public IRoute Route { get; internal set; } /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index 99116cc..b2fec2f 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -14,7 +14,6 @@ using WebExpress.WebCore.WebPage.Model; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPage { @@ -303,8 +302,6 @@ private void Register(IPluginContext pluginContext, IEnumerable(); var conditions = new List(); @@ -320,14 +317,6 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) - { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); - } - else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) - { - contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType == typeof(IncludeSubPathsAttribute)) { includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); @@ -361,32 +350,36 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType) + }; + var pageItem = new PageItem(_componentHub.EndpointManager) { EndpointId = new ComponentId(id), PluginContext = pluginContext, ApplicationContext = applicationContext, + PageContext = pageContext, Title = title, - ParentType = parent, PageClass = pageType, Scopes = scopes, Cache = cache, Conditions = conditions, - ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, - PathSegment = segment?.ToPathSegment() ?? new UriPathSegmentConstant(pageType.Name.ToLower()), Attributes = attributes.Select(x => x.AttributeType) }; @@ -398,7 +391,7 @@ private void Register(IPluginContext pluginContext, IEnumerable /// The uri of the request. /// - public UriResource Uri => Request?.Uri; + public UriEndpoint Uri => Request?.Uri; /// /// Returns the culture. diff --git a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs index e5ab594..59beca1 100644 --- a/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs +++ b/src/WebExpress.WebCore/WebPage/VisualTreeContext.cs @@ -16,7 +16,7 @@ public class VisualTreeContext : IVisualTreeContext /// /// The uri of the request. /// - public UriResource Uri => RenderContext?.Request?.Uri; + public UriEndpoint Uri => RenderContext?.Request?.Uri; /// /// Return or sets the render context. diff --git a/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs b/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs index 758aad9..aec3cdd 100644 --- a/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/IPluginContext.cs @@ -1,6 +1,6 @@ using System.Reflection; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebPlugin { @@ -52,11 +52,6 @@ public interface IPluginContext : IContext /// /// Returns the icon of the plugin. /// - UriResource Icon { get; } - - /// - /// Returns the host context. - /// - IHttpServerContext Host { get; } + IRoute Icon { get; } } } diff --git a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs index 1f6ef97..039769b 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginContext.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginContext.cs @@ -1,6 +1,6 @@ using System.Reflection; using WebExpress.WebCore.WebComponent; -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebEndpoint; namespace WebExpress.WebCore.WebPlugin { @@ -52,12 +52,7 @@ public class PluginContext : IPluginContext /// /// Returns the icon of the plugin. /// - public UriResource Icon { get; internal set; } - - /// - /// Returns the host context. - /// - public IHttpServerContext Host { get; internal set; } + public IRoute Icon { get; internal set; } /// /// Initializes a new instance of the class. diff --git a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs index 71588f1..e3e7edd 100644 --- a/src/WebExpress.WebCore/WebPlugin/PluginManager.cs +++ b/src/WebExpress.WebCore/WebPlugin/PluginManager.cs @@ -9,9 +9,9 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin.Model; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebPlugin { @@ -177,8 +177,7 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginName = assembly.GetName().Name.ToLower(), Manufacturer = assembly.GetCustomAttribute()?.Company, Copyright = assembly.GetCustomAttribute()?.Copyright, - Version = assembly.GetCustomAttribute()?.InformationalVersion, - Host = _httpServerContext + Version = assembly.GetCustomAttribute()?.InformationalVersion }; if (!_dictionary.ContainsKey(id)) @@ -278,10 +277,9 @@ private IEnumerable Register(Assembly assembly, PluginLoadContex PluginName = name, Manufacturer = type.Assembly.GetCustomAttribute()?.Company, Copyright = type.Assembly.GetCustomAttribute()?.Copyright, - Icon = UriResource.Combine(_httpServerContext?.ContextPath, icon), + Icon = RouteEndpoint.Combine(_httpServerContext?.ContextPath, icon), Description = description, - Version = type.Assembly.GetCustomAttribute()?.InformationalVersion, - Host = _httpServerContext + Version = type.Assembly.GetCustomAttribute()?.InformationalVersion }; hasUnfulfilledDependencies = HasUnfulfilledDependencies(id, dependencies.Select(x => new ComponentId(x))); diff --git a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs index 7ca8890..c036f88 100644 --- a/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs +++ b/src/WebExpress.WebCore/WebResource/Model/ResourceItem.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource.Model @@ -11,27 +14,35 @@ namespace WebExpress.WebCore.WebResource.Model /// internal class ResourceItem : IDisposable { - private readonly IResourceManager _resourceManager; + /// + /// Returns the endpoint id. + /// + public IComponentId EndpointId { get; internal set; } + + /// + /// Returns the context of the associated plugin. + /// + public IPluginContext PluginContext { get; internal set; } /// - /// Returns or sets the parent type. + /// Returns the application context. /// - public Type ParentType { get; set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns or sets the type of resource. /// - public Type ResourceClass { get; set; } + public Type ResourceClass { get; internal set; } /// /// Returns or sets the instance of the resource, if the resource is cached, otherwise null. /// - public IEndpoint Instance { get; set; } + public IEndpoint Instance { get; internal set; } /// /// Returns or sets the paths of the resource. /// - public UriResource ContextPath { get; set; } + public UriEndpoint ContextPath { get; internal set; } /// /// Returns or sets the path segment. @@ -41,17 +52,22 @@ internal class ResourceItem : IDisposable /// /// Returns or sets whether all subpaths should be taken into sitemap. /// - public bool IncludeSubPaths { get; set; } + public bool IncludeSubPaths { get; internal set; } /// /// Returns the conditions that must be met for the resource to be active. /// - public IEnumerable Conditions { get; set; } + public IEnumerable Conditions { get; internal set; } /// /// Returns whether the resource is created once and reused each time it is called. /// - public bool Cache { get; set; } + public bool Cache { get; internal set; } + + /// + /// Returns the attributes associated with the page. + /// + public IEnumerable Attributes { get; internal set; } /// /// Returns the resource context. @@ -64,7 +80,6 @@ internal class ResourceItem : IDisposable /// The resource manager. internal ResourceItem(IResourceManager resourceManager) { - _resourceManager = resourceManager; } /// @@ -72,7 +87,6 @@ internal ResourceItem(IResourceManager resourceManager) /// public void Dispose() { - } /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs index 301db97..5db57f2 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceAsset.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceAsset.cs @@ -45,7 +45,6 @@ public override Response Process(Request request) var assembly = ResourceContext.PluginContext.Assembly; var buf = assembly.GetManifestResourceNames().ToList(); var resources = assembly.GetManifestResourceNames().Where(x => x.StartsWith(AssetDirectory, System.StringComparison.OrdinalIgnoreCase)); - var contextPath = ResourceContext.ContextPath; var url = request.Uri.ExtendedPath.ToString(); var fileName = Path.GetFileName(url); var file = string.Join('.', AssetDirectory.Trim('.'), "assets", url.Replace("/", ".").Trim('.')); diff --git a/src/WebExpress.WebCore/WebResource/ResourceContext.cs b/src/WebExpress.WebCore/WebResource/ResourceContext.cs index b2b1087..09249e7 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceContext.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceContext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -15,10 +14,10 @@ namespace WebExpress.WebCore.WebResource /// public class ResourceContext : IResourceContext { - private readonly IEndpointManager _endpointManager; - private readonly Type _parentType; - private readonly UriResource _contextPath; - private readonly IUriPathSegment _pathSegment; + /// + /// Returns the resource id. + /// + public IComponentId EndpointId { get; internal set; } /// /// Returns the associated plugin context. @@ -35,17 +34,6 @@ public class ResourceContext : IResourceContext /// public IEnumerable Conditions { get; internal set; } = []; - /// - /// Returns the resource id. - /// - public IComponentId EndpointId { get; internal set; } - - /// - /// Returns the parent or null if not used. - /// - public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) - .FirstOrDefault(); - /// /// Returns whether the resource is created once and reused each time it is called. /// @@ -59,38 +47,23 @@ public class ResourceContext : IResourceContext /// /// Returns the context path. /// - public UriResource ContextPath - { - get - { - var parentContext = ParentContext; - if (parentContext != null) - { - return UriResource.Combine(ParentContext?.Uri, _contextPath); - } + public UriEndpoint ContextPath { get; internal set; } - return UriResource.Combine(ApplicationContext.ContextPath, _contextPath); - } - } + /// + /// Returns the internal routing path for the endpoint. + /// + public IRoute Route { get; internal set; } /// - /// Returns the uri. + /// Returns the attributes associated with the page. /// - public UriResource Uri => ContextPath.Append(_pathSegment); + public IEnumerable Attributes { get; internal set; } /// /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. /// - /// The endpoint manager responsible for managing endpoints. - /// The type of the parent resource. - /// The context path of the resource. - /// The path segment of the resource. - public ResourceContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) + public ResourceContext() { - _endpointManager = endpointManager; - _parentType = parentType; - _contextPath = contextPath; - _pathSegment = pathSegment; } /// diff --git a/src/WebExpress.WebCore/WebResource/ResourceFile.cs b/src/WebExpress.WebCore/WebResource/ResourceFile.cs index 32de325..c43d0cc 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceFile.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceFile.cs @@ -37,8 +37,7 @@ public override Response Process(Request request) { lock (Gard) { - var contextPath = ResourceContext.ContextPath; - var url = request.Uri.ToString()[contextPath.ToString().Length..]; + var url = request.Uri.ToString()[ResourceContext.Route.ToString().Length..]; var path = System.IO.Path.GetFullPath(RootDirectory + url); diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 4a51f3d..1ebcaa8 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -11,7 +11,6 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebResource.Model; using WebExpress.WebCore.WebStatusPage; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebResource { @@ -130,11 +129,13 @@ private void Register(IPluginContext pluginContext, IEnumerable(); var cache = false; + var attributes = resourceType.CustomAttributes + .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && + !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); foreach (var customAttribute in resourceType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) @@ -143,14 +144,6 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) - { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); - } - else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) - { - contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType == typeof(IncludeSubPathsAttribute)) { includeSubPaths = Convert.ToBoolean(customAttribute.ConstructorArguments.FirstOrDefault().Value); @@ -166,39 +159,38 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType) }; + var resourceItem = new ResourceItem(_componentHub.ResourceManager) { - ParentType = parent, - ResourceClass = resourceType, + EndpointId = new ComponentId(id), + PluginContext = pluginContext, + ApplicationContext = applicationContext, ResourceContext = resourceContext, + ResourceClass = resourceType, Cache = cache, Conditions = conditions, - ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, - PathSegment = segment?.ToPathSegment() ?? new UriPathSegmentConstant(resourceType.Name.ToLower()), + Attributes = attributes.Select(x => x.AttributeType) }; if (_dictionary.AddResourceItem(pluginContext, applicationContext, resourceItem)) { - OnAddResource(resourceContext); + OnAddResource(resourceItem.ResourceContext); _httpServerContext?.Log.Debug( I18N.Translate( "webexpress.webcore:resourcemanager.addresource", diff --git a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs index b51acae..ff43210 100644 --- a/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs +++ b/src/WebExpress.WebCore/WebRestAPI/Model/RestApiItem.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Generic; +using WebExpress.WebCore.WebApplication; +using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebRestApi.Model @@ -10,27 +14,35 @@ namespace WebExpress.WebCore.WebRestApi.Model /// internal class RestApiItem : IDisposable { - private readonly IRestApiManager _restApiManager; + /// + /// Returns the endpoint id. + /// + public IComponentId EndpointId { get; internal set; } + + /// + /// Returns the associated plugin context. + /// + public IPluginContext PluginContext { get; internal set; } /// - /// Returns or sets the parent type. + /// Returns the corresponding application context. /// - public Type ParentType { get; set; } + public IApplicationContext ApplicationContext { get; internal set; } /// /// Returns or sets the type of rest api resource. /// - public Type RestApiClass { get; set; } + public Type RestApiClass { get; internal set; } /// /// Returns or sets the instance of the rest api resource, if the rest api resource is cached, otherwise null. /// - public IRestApi Instance { get; set; } + public IRestApi Instance { get; internal set; } /// /// Returns or sets the paths of the resource. /// - public UriResource ContextPath { get; set; } + public UriEndpoint ContextPath { get; internal set; } /// /// Returns or sets the path segment. @@ -40,32 +52,32 @@ internal class RestApiItem : IDisposable /// /// Returns or sets whether all subpaths should be taken into sitemap. /// - public bool IncludeSubPaths { get; set; } + public bool IncludeSubPaths { get; internal set; } /// /// Returns the conditions that must be met for the rest api resource to be active. /// - public IEnumerable Conditions { get; set; } + public IEnumerable Conditions { get; internal set; } /// /// Returns the crud methods. /// - public IEnumerable Methods { get; set; } + public IEnumerable Methods { get; internal set; } /// /// Returns the version number of the rest api. /// - public uint Version { get; set; } + public uint Version { get; internal set; } /// /// Returns whether the resource is created once and reused each time it is called. /// - public bool Cache { get; set; } + public bool Cache { get; internal set; } /// - /// Returns whether it is a optional rest api resource. + /// Returns the attributes associated with the page. /// - public bool Optional { get; set; } + public IEnumerable Attributes { get; internal set; } /// /// Returns the rest api contexts. @@ -75,10 +87,9 @@ internal class RestApiItem : IDisposable /// /// Initializes a new instance of the class. /// - /// The rest api manager. - internal RestApiItem(IRestApiManager restApiManager) + /// The endpoint manager responsible for managing endpoints. + internal RestApiItem(IEndpointManager endpointManager) { - _restApiManager = restApiManager; } /// diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs index 8d6279d..3dc09ce 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiContext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -15,11 +14,6 @@ namespace WebExpress.WebCore.WebRestApi /// public class RestApiContext : IRestApiContext { - private readonly IEndpointManager _endpointManager; - private readonly Type _parentType; - private readonly UriResource _contextPath; - private readonly IUriPathSegment _pathSegment; - /// /// Returns the associated plugin context. /// @@ -45,12 +39,6 @@ public class RestApiContext : IRestApiContext /// public IComponentId EndpointId { get; internal set; } - /// - /// Returns the parent or null if not used. - /// - public IEndpointContext ParentContext => _endpointManager.GetEndpoints(_parentType, ApplicationContext) - .FirstOrDefault(); - /// /// Returns the version number of the rest api. /// @@ -67,40 +55,25 @@ public class RestApiContext : IRestApiContext public bool IncludeSubPaths { get; internal set; } /// - /// Returns the context path. + /// Returns the attributes associated with the page. /// - public UriResource ContextPath - { - get - { - var parentContext = ParentContext; - if (parentContext != null) - { - return UriResource.Combine(ParentContext?.Uri, _contextPath, Version.ToString()); - } + public IEnumerable Attributes { get; internal set; } - return UriResource.Combine(ApplicationContext?.ContextPath, _contextPath, Version.ToString()); - } - } + /// + /// Returns the context path. + /// + public UriEndpoint ContextPath { get; internal set; } /// - /// Returns the uri. + /// Returns the internal routing path for the endpoint. /// - public UriResource Uri => ContextPath.Append(_pathSegment); + public IRoute Route { get; internal set; } /// /// Initializes a new instance of the class with the specified parent type and context path. /// - /// The endpoint manager responsible for managing endpoints. - /// The type of the parent resource. - /// The context path of the resource. - /// The path segment of the resource. - public RestApiContext(IEndpointManager endpointManager, Type parentType, UriResource contextPath, IUriPathSegment pathSegment) + public RestApiContext() { - _endpointManager = endpointManager; - _parentType = parentType; - _contextPath = contextPath; - _pathSegment = pathSegment; } /// diff --git a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs index 26c0926..ad65b6b 100644 --- a/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs +++ b/src/WebExpress.WebCore/WebRestAPI/RestApiManager.cs @@ -303,31 +303,29 @@ private void Register(IPluginContext pluginContext, IEnumerable x.IsClass == true && x.IsSealed && x.IsPublic) .Where(x => x.GetInterface(typeof(IRestApi).Name) != null)) { - var id = resrApiType.FullName?.ToLower(); + var id = restApiType.FullName?.ToLower(); var segment = default(ISegmentAttribute); - var title = resrApiType.Name; - var parent = default(Type); + var title = restApiType.Name; var contextPath = string.Empty; var includeSubPaths = false; var conditions = new List(); var cache = false; var methods = new List(); - var version = 1u; + var version = -1; + var attributes = restApiType.CustomAttributes + .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && + !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); - foreach (var customAttribute in resrApiType.CustomAttributes + foreach (var customAttribute in restApiType.CustomAttributes .Where(x => x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)))) { if (customAttribute.AttributeType.GetInterfaces().Contains(typeof(ISegmentAttribute))) { - segment = resrApiType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; - } - else if (customAttribute.AttributeType.Name == typeof(ParentAttribute<>).Name && customAttribute.AttributeType.Namespace == typeof(ParentAttribute<>).Namespace) - { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + segment = restApiType.GetCustomAttributes(customAttribute.AttributeType, false).FirstOrDefault() as ISegmentAttribute; } else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) { @@ -337,12 +335,14 @@ private void Register(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) + else if (customAttribute.AttributeType.Name == typeof(ConditionAttribute<>).Name + && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) { var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); conditions.Add(Activator.CreateInstance(condition) as ICondition); } - else if (customAttribute.AttributeType.Name == typeof(MethodAttribute).Name && customAttribute.AttributeType.Namespace == typeof(MethodAttribute).Namespace) + else if (customAttribute.AttributeType.Name == typeof(MethodAttribute).Name + && customAttribute.AttributeType.Namespace == typeof(MethodAttribute).Namespace) { var method = (CrudMethod)customAttribute.ConstructorArguments.FirstOrDefault().Value; methods.Add(method); @@ -353,63 +353,67 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IRestApiAttribute)))) { - if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) + if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name + && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) { - version = Convert.ToUInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); + version = Convert.ToInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); } - - } - - if (segment == default && parent == default && contextPath == "") - { - var assemblyName = assembly.GetName().Name; - var fullClassName = resrApiType.FullName; - var path = fullClassName[(assemblyName.Length + 1)..^(resrApiType.Name.Length + 1)]; - - contextPath = "/" + path.ToLower().Replace('.', '/'); } // assign the rest api to existing applications foreach (var applicationContext in applicationContexts) { - var restApiContext = new RestApiContext(_componentHub.EndpointManager, parent, new UriResource(contextPath), segment.ToPathSegment()) + var routePath = EndpointManager.CreateEndpointRoute + ( + restApiType, + applicationContext, + segment, + [new UriPathSegmentConstant("api"), new UriPathSegmentVariableInt($"{version}") { VariableName = "apiVersion" }], + ["api", "restapi", "rest"] + ); + var versionPath = version < 1 ? "" : version.ToString(); + + var restApiContext = new RestApiContext() { - EndpointId = new ComponentId(resrApiType.FullName), + EndpointId = new ComponentId(id), PluginContext = pluginContext, ApplicationContext = applicationContext, + Route = routePath, Cache = cache, Conditions = conditions, - Methods = methods, - Version = version, - IncludeSubPaths = includeSubPaths + IncludeSubPaths = includeSubPaths, + Attributes = attributes.Select(x => x.AttributeType), + Version = version < 1 ? 1u : (uint)version, + Methods = methods.Distinct() }; - var restApiItem = new RestApiItem(_componentHub.RestApiManager) + var restApiItem = new RestApiItem(_componentHub.EndpointManager) { - ParentType = parent, + EndpointId = new ComponentId(restApiType.FullName), + PluginContext = pluginContext, + ApplicationContext = applicationContext, RestApiContext = restApiContext, - RestApiClass = resrApiType, + RestApiClass = restApiType, Methods = methods.Distinct(), - Version = version, + Version = version < 1 ? 1u : (uint)version, Cache = cache, Conditions = conditions, - ContextPath = new UriResource(contextPath), IncludeSubPaths = includeSubPaths, - PathSegment = segment?.ToPathSegment() ?? new UriPathSegmentConstant(resrApiType.Name.ToLower()) + Attributes = attributes.Select(x => x.AttributeType) }; if (_dictionary.AddRestApiItem(pluginContext, applicationContext, restApiItem)) { - OnAddRestApi(restApiContext); + OnAddRestApi(restApiItem.RestApiContext); _httpServerContext?.Log.Debug ( I18N.Translate ( - "webexpress.webcore:restapimanager.addresource", + "webexpress.webcore:restapimanager.addrestapi", id, applicationContext.ApplicationId ) diff --git a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs index 2292b13..ee6f3b3 100644 --- a/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs +++ b/src/WebExpress.WebCore/WebSettingPage/Model/SettingPageItem.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebCondition; @@ -16,9 +15,6 @@ namespace WebExpress.WebCore.WebSettingPage.Model /// public class SettingPageItem : IDisposable { - private readonly IEndpointManager _endpointManager; - private SettingPageContext _settingPageContext; - /// /// Returns the endpoint id. /// @@ -37,40 +33,7 @@ public class SettingPageItem : IDisposable /// /// Returns the setting page context. /// - public ISettingPageContext SettingPageContext - { - get - { - _settingPageContext ??= new SettingPageContext() - { - PageTitle = PageTitle, - EndpointId = EndpointId, - PluginContext = PluginContext, - ApplicationContext = ApplicationContext, - Cache = Cache, - Scopes = Scopes, - Conditions = Conditions, - IncludeSubPaths = IncludeSubPaths, - Attributes = Attributes, - SettingGroup = SettingGroup, - Icon = Icon, - }; - - var parentContext = _endpointManager.GetEndpoints(ParentType, ApplicationContext) - .FirstOrDefault(); - - var contextPath = UriResource.Combine - ( - parentContext?.Uri ?? ApplicationContext.ContextPath, ContextPath - ); - - _settingPageContext.ParentContext = parentContext; - _settingPageContext.ContextPath = contextPath; - _settingPageContext.Uri = contextPath.Append(PathSegment); - - return _settingPageContext; - } - } + public ISettingPageContext SettingPageContext { get; internal set; } /// /// Returns the class type of the setting page. @@ -90,7 +53,7 @@ public ISettingPageContext SettingPageContext /// /// Returns or sets the paths of the resource. /// - public UriResource ContextPath { get; set; } + public UriEndpoint ContextPath { get; set; } /// /// Returns or sets the path segment. @@ -160,7 +123,6 @@ public ISettingPageContext SettingPageContext /// The endpoint manager responsible for managing endpoints. internal SettingPageItem(IEndpointManager endpointManager) { - _endpointManager = endpointManager; } /// diff --git a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs index be64a45..4aa0060 100644 --- a/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs +++ b/src/WebExpress.WebCore/WebSettingPage/SettingPageManager.cs @@ -8,6 +8,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebIcon; using WebExpress.WebCore.WebMessage; @@ -15,7 +16,6 @@ using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebScope; using WebExpress.WebCore.WebSettingPage.Model; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebSettingPage { @@ -434,7 +434,7 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable(); var contextPath = string.Empty; var scopes = new List(); var group = default(Type); @@ -455,14 +455,6 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable)) - { - parent = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); - } - else if (customAttribute.AttributeType == typeof(ContextPathAttribute)) - { - contextPath = customAttribute.ConstructorArguments.FirstOrDefault().Value?.ToString(); - } else if (customAttribute.AttributeType.IsGenericType && customAttribute.AttributeType.GetGenericTypeDefinition() == typeof(SettingGroupAttribute<>)) { group = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); @@ -488,6 +480,11 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable).Name && customAttribute.AttributeType.Namespace == typeof(ConditionAttribute<>).Namespace) + { + var condition = customAttribute.AttributeType.GenericTypeArguments.FirstOrDefault(); + conditions.Add(Activator.CreateInstance(condition) as ICondition); + } } if (group == default) @@ -520,36 +517,38 @@ private void RegisterPage(IPluginContext pluginContext, IEnumerable x.AttributeType), PageTitle = title, Scopes = scopes, - SettingGroupType = group?.GetType(), + SettingGroup = _groupDictionary.GetSettingGroup(applicationContext, group), Section = section, Hide = hide, - Icon = icon, - Cache = cache, - ContextPath = new UriResource(contextPath), + Icon = icon + }; + + // create meta information of the setting page + var settingPageItem = new SettingPageItem(_componentHub.EndpointManager) + { + EndpointId = new ComponentId(id), + PluginContext = pluginContext, + ApplicationContext = applicationContext, + SettingPageContext = settingPageContext, + SettingPageClass = settingPageType, + SettingGroupType = group?.GetType(), IncludeSubPaths = includeSubPaths, - PathSegment = segment?.ToPathSegment() ?? new UriPathSegmentConstant(settingPageType.Name.ToLower()), Attributes = attributes.Select(x => x.AttributeType) }; diff --git a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs index 681d2a6..f702b3d 100644 --- a/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/ISitemapManager.cs @@ -32,38 +32,31 @@ public interface ISitemapManager : IComponentManager SearchResult SearchResource(Uri requestUri, SearchContext searchContext); /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). - /// - /// Returns the uri taking into account the context or null. - UriResource GetUri(params Parameter[] parameters) - where TEndpoint : IEndpoint; - - /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. - /// - /// The resource type. - /// The parameters to be considered for the URI. + /// The class from which the URI is to be determined. URI route must not have any dynamic components (such as '/a/guid/b'). + /// The application context. + /// The parameters to be considered for the uri. /// Returns the URI taking into account the context, or null if no valid URI is found. - UriResource GetUri(Type resourceType, params Parameter[] parameters); + IUri GetUri(IApplicationContext applicationContext, params Parameter[] parameters) + where TEndpoint : IEndpoint; /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). + /// The endpoint type. /// The application context. - /// Returns the uri taking into account the context or null. - UriResource GetUri(IApplicationContext applicationContext) - where TEndpoint : IEndpoint; + /// The parameters to be considered for the uri. + /// Returns the URI taking into account the context, or null if no valid URI is found. + IUri GetUri(Type endpointType, IApplicationContext applicationContext, params Parameter[] parameters); /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a/guid/b'). + /// The class from which the URI is to be determined. URI route must not have any dynamic components (such as '/a/guid/b'). /// The endpoint context. - /// Returns the uri taking into account the context or null. - UriResource GetUri(IEndpointContext endpointContext) + /// Returns the URI taking into account the context, or null if no valid URI is found. + IUri GetUri(IEndpointContext endpointContext) where TEndpoint : IEndpoint; /// @@ -71,6 +64,6 @@ UriResource GetUri(IEndpointContext endpointContext) /// /// The URI resource to search for. /// The endpoint context if found, otherwise null. - IEndpointContext GetEndpoint(UriResource uri); + IEndpointContext GetEndpoint(UriEndpoint uri); } } diff --git a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs index c477dfa..f1a271b 100644 --- a/src/WebExpress.WebCore/WebSitemap/SearchResult.cs +++ b/src/WebExpress.WebCore/WebSitemap/SearchResult.cs @@ -35,6 +35,6 @@ public class SearchResult /// Returns the uri. /// /// The uri. - public UriResource Uri { get; internal set; } + public UriEndpoint Uri { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs index 5aafb47..4635707 100644 --- a/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs +++ b/src/WebExpress.WebCore/WebSitemap/SitemapManager.cs @@ -21,6 +21,7 @@ public sealed class SitemapManager : ISitemapManager, ISystemComponent private SitemapNode _root = new(); private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; + private readonly IUri _serverUri; /// /// Returns the side map. @@ -39,6 +40,8 @@ private SitemapManager(IComponentHub componentHub, IHttpServerContext httpServer { _componentHub = componentHub; _httpServerContext = httpServerContext; + _serverUri = new UriEndpoint(_httpServerContext.Endpoints.FirstOrDefault(e => e.Uri.StartsWith("https"))?.ToString() + ?? _httpServerContext.Endpoints.FirstOrDefault()?.ToString() ?? ""); _httpServerContext.Log.Debug ( @@ -65,7 +68,7 @@ public void Refresh() ApplicationContext = x, x.ContextPath.PathSegments }) - .OrderBy(x => x.PathSegments.Count); + .OrderBy(x => x.PathSegments.Count()); foreach (var application in applications) { @@ -81,9 +84,9 @@ public void Refresh() .Select(x => new { EndpointContext = x, - x.Uri.PathSegments + x.Route.PathSegments }) - .OrderBy(x => x.PathSegments.Count); + .OrderBy(x => x.PathSegments.Count()); foreach (var item in resources) { @@ -129,63 +132,45 @@ public SearchResult SearchResource(Uri requestUri, SearchContext searchContext) } /// - /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context + /// in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). - /// - /// Returns the uri taking into account the context or null. - public UriResource GetUri(params Parameter[] parameters) where T : IEndpoint - { - var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T)); - - var node = _root.GetPreOrder() - .Where(x => endpointContexts.Contains(x.EndpointContext)) - .FirstOrDefault(); - - return node?.EndpointContext?.Uri.SetParameters(parameters); - } - - /// - /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. - /// - /// The resource type. - /// The parameters to be considered for the URI. + /// The class from which the URI is to be determined. URI route must not have any dynamic components (such as '/a/guid/b'). + /// The application context. + /// The parameters to be considered for the uri. /// Returns the URI taking into account the context, or null if no valid URI is found. - public UriResource GetUri(Type resourceType, params Parameter[] parameters) + public IUri GetUri(IApplicationContext applicationContext, params Parameter[] parameters) + where TEndpoint : IEndpoint { - var endpointContexts = _componentHub.EndpointManager.GetEndpoints(resourceType); - - var node = _root.GetPreOrder() - .Where(x => endpointContexts.Contains(x.EndpointContext)) - .FirstOrDefault(); - - return node?.EndpointContext?.Uri.SetParameters(parameters); + return GetUri(typeof(TEndpoint), applicationContext, parameters); } /// - /// Determines the uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The endpoint type. /// The application context. - /// Returns the uri taking into account the context or null. - public UriResource GetUri(IApplicationContext applicationContext) where T : IEndpoint + /// The parameters to be considered for the uri. + /// Returns the URI taking into account the context, or null if no valid URI is found. + public IUri GetUri(Type endpointType, IApplicationContext applicationContext, params Parameter[] parameters) { - var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(T), applicationContext); + var endpointContexts = _componentHub.EndpointManager.GetEndpoints(endpointType, applicationContext); var node = _root.GetPreOrder() .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.EndpointContext?.Uri; + return new UriEndpoint(_serverUri, node?.EndpointContext?.Route.PathSegments, null).SetParameters(parameters); } /// - /// Determines the Uri from the sitemap of a class, taking into account the context in which the uri is valid. + /// Returns the URI for this type based on the sitemap configuration, taking into account the specific context in which the URI is valid. /// - /// The class from which the uri is to be determined. The class uri must not have any dynamic components (such as '/a//b'). + /// The class from which the URI is to be determined. URI route must not have any dynamic components (such as '/a/guid/b'). /// The endpoint context. - /// Returns the uri taking into account the context or null. - public UriResource GetUri(IEndpointContext endpointContext) where TEnpoint : IEndpoint + /// Returns the URI taking into account the context, or null if no valid URI is found. + public IUri GetUri(IEndpointContext endpointContext) + where TEnpoint : IEndpoint { var endpointContexts = _componentHub.EndpointManager.GetEndpoints(typeof(TEnpoint), endpointContext.ApplicationContext) .Where(x => x.EndpointId.Equals(endpointContext.EndpointId)); @@ -194,7 +179,7 @@ public UriResource GetUri(IEndpointContext endpointContext) where TEnp .Where(x => endpointContexts.Contains(x.EndpointContext)) .FirstOrDefault(); - return node?.EndpointContext?.Uri; + return new UriEndpoint(_serverUri, node?.EndpointContext?.Route.PathSegments, null); } /// @@ -202,7 +187,7 @@ public UriResource GetUri(IEndpointContext endpointContext) where TEnp /// /// The URI resource to search for. /// The endpoint context if found, otherwise null. - public IEndpointContext GetEndpoint(UriResource uri) + public IEndpointContext GetEndpoint(UriEndpoint uri) { var variables = new Dictionary(); var result = SearchNode @@ -417,7 +402,7 @@ SearchContext searchContext { EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource([.. outPathSegments]) + Uri = new UriEndpoint([.. outPathSegments]) }; } else if (node.IsLeaf && nextPathSegment != null && node.EndpointContext != null && node.EndpointContext.IncludeSubPaths) @@ -426,7 +411,7 @@ SearchContext searchContext { EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource([.. outPathSegments]) + Uri = new UriEndpoint([.. outPathSegments]) }; } @@ -441,7 +426,7 @@ SearchContext searchContext { EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriResource([.. outPathSegments]) + Uri = new UriEndpoint([.. outPathSegments]) }; } diff --git a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs index 529c456..55f24df 100644 --- a/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/IStatusPageContext.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { @@ -38,6 +38,6 @@ public interface IStatusPageContext : IContext /// /// Returns the status icon. /// - UriResource StatusIcon { get; } + IRoute StatusIcon { get; } } } diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs index 40b34a5..8a4c416 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageContext.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { @@ -38,7 +38,7 @@ public class StatusPageContext : IStatusPageContext /// /// Returns the status icon. /// - public UriResource StatusIcon { get; internal set; } + public IRoute StatusIcon { get; internal set; } /// /// Returns a string that represents the current object. diff --git a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs index 0658ddf..a57c176 100644 --- a/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs +++ b/src/WebExpress.WebCore/WebStatusPage/StatusPageManager.cs @@ -14,7 +14,6 @@ using WebExpress.WebCore.WebPage; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebStatusPage.Model; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebStatusPage { @@ -157,7 +156,7 @@ private void Register(IPluginContext pluginContext, IEnumerable().StatusCode; var statusPageContext = new StatusPageContext() { diff --git a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs index 6ee7a61..d58e729 100644 --- a/src/WebExpress.WebCore/WebTheme/IThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/IThemeContext.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebTheme { @@ -28,7 +28,7 @@ public interface IThemeContext : IContext /// /// Returns the image associated with the theme. /// - UriResource Image { get; } + IRoute Image { get; } /// /// Returns the name of the theme. @@ -46,8 +46,8 @@ public interface IThemeContext : IContext ThemeMode ThemeMode { get; } /// - /// Returns the URI resource for the css theme style. + /// Returns the route resource for the css theme style. /// - UriResource ThemeStyle { get; } + IRoute ThemeStyle { get; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs index 9456cc3..4fc09b2 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeContext.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeContext.cs @@ -1,7 +1,7 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebTheme { @@ -28,7 +28,7 @@ public class ThemeContext : IThemeContext /// /// Returns the image associated with the theme. /// - public UriResource Image { get; internal set; } + public IRoute Image { get; internal set; } /// /// Returns the name of the theme. @@ -46,8 +46,8 @@ public class ThemeContext : IThemeContext public ThemeMode ThemeMode { get; internal set; } /// - /// Returns the URI resource for the css theme style. + /// Returns the route resource for the css theme style. /// - public UriResource ThemeStyle { get; internal set; } + public IRoute ThemeStyle { get; internal set; } } } diff --git a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs index ae11b42..928fc3a 100644 --- a/src/WebExpress.WebCore/WebTheme/ThemeManager.cs +++ b/src/WebExpress.WebCore/WebTheme/ThemeManager.cs @@ -6,10 +6,10 @@ using WebExpress.WebCore.WebApplication; using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebComponent; +using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebLog; using WebExpress.WebCore.WebPlugin; using WebExpress.WebCore.WebTheme.Model; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebTheme { @@ -199,9 +199,9 @@ private void Register(IPluginContext pluginContext, IEnumerable + /// An Uri represents a complete, fully qualified Uniform Resource Identifier (URI) that uniquely identifies a endpoint. + /// + /// This interface encapsulates all components of a typical URI, such as the scheme (e.g., "http", "https"), + /// the authority (e.g., "example.com"), path segments, query parameters, and fragment. It provides the external + /// address used for resource identification and linking (e.g., "http://example.com/users/123"). + /// + public interface IUri + { + /// + /// The scheme (e.g. Http, FTP). + /// + UriScheme Scheme { get; } + + /// + /// The authority (e.g. user@example.com:8080). + /// + UriAuthority Authority { get; } + + /// + /// The path (e.g. /over/there). + /// + IEnumerable PathSegments { get; } + + /// + /// Returns the extended segment of the endpoint's path, which is included only when the endpoint class has the IncludeSubPaths attribute enabled. + /// For example, if the core endpoint is "http://example.com/server/app/endpoint" and the extended segment is "extended", + /// the complete URI becomes "http://example.com/server/app/endpoint/extended". In this case, the property returns the "extended" part. + /// + IUri ExtendedPath { get; } + + /// + /// The query part (e.g. ?title=Uniform_Resource_Identifier). + /// + IEnumerable Query { get; } + + /// + /// References a position within a resource (e.g. #Anchor). + /// + string Fragment { get; } + + /// + /// Returns the display string of the Uri + /// + string Display { get; } + + /// + /// Determines if the uri is empty. + /// + bool Empty { get; } + + /// + /// Retrieves the base URI of the endpoint. When the IncludeSubPaths attribute is enabled on the endpoint class, + /// the complete URI may include extra path segments. For example, the core endpoint could be + /// "http://example.com/server/app/endpoint", but with IncludeSubPaths enabled, the full URI might become + /// "http://example.com/server/app/endpoint/extended". In such cases, this property returns only the base URI: + /// "http://example.com/server/app/endpoint". + /// + IUri EndpointRoot { get; } + + /// + /// Returns the root of the application. + /// + IUri ApplicationRoot { get; } + + /// + /// Returns the root of the server. + /// + IUri ServerRoot { get; } + + /// + /// Determines if the Uri is the root. + /// + bool IsRoot { get; } + + /// + /// Checks if it is a relative uri. + /// + bool IsRelative { get; } + + /// + /// Retrieves a collection of variables represented as key-value pairs. + /// + IDictionary Parameters { get; } + + /// + /// Concatenates the given path segment to the current URI and returns a new instance of IUri with the updated path. + /// + /// The path segment to be concatenated with the existing URI. + /// A new IUri instance representing the URI after concatenation. + IUri Concat(string segment); + + /// + /// Concatenates the given path segment to the current URI and returns a new instance of IUri with the updated path. + /// + /// An array of path segments to be concatenated to the existing URI. + /// A new IUri instance representing the URI after concatenation. + IUri Concat(params IUriPathSegment[] segments); + + /// + /// Return a shortened uri containing n-elements. + /// count greater than 0 count elements are included + /// count less than 0 count elements are truncated + /// count = 0 an empty uri is returned + /// + /// The count of elements to include or truncate. + /// The sub uri with the specified number of elements. + IUri Take(int count); + + /// + /// Return a shortened uri by not including the first n elements. + /// count greater than 0 count elements are skipped + /// count less than or equals 0 an empty Uri is returned + /// + /// The count of elements to skip. + /// The sub uri after skipping the specified number of elements. + IUri Skip(int count); + + /// + /// Determines whether the given segment is part of the uri. + /// + /// The segment to be tested. + /// true if successful, false otherwise. + bool Contains(string segment); + + /// + /// Checks whether a given uri is part of that uri. + /// + /// The Uri to be checked. + /// true if part of the uri, false otherwise. + bool StartsWith(IUri uri); + } +} diff --git a/src/WebExpress.WebCore/WebUri/UriResource.cs b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs similarity index 67% rename from src/WebExpress.WebCore/WebUri/UriResource.cs rename to src/WebExpress.WebCore/WebUri/UriEndpoint.cs index 3b4acea..f0236fc 100644 --- a/src/WebExpress.WebCore/WebUri/UriResource.cs +++ b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs @@ -6,9 +6,13 @@ namespace WebExpress.WebCore.WebUri { /// - /// A resource uri (e.g. /image.png). + /// An Uri represents a complete, fully qualified Uniform Resource Identifier (URI) that uniquely identifies a endpoint. + /// + /// This interface encapsulates all components of a typical URI, such as the scheme (e.g., "http", "https"), + /// the authority (e.g., "example.com"), path segments, query parameters, and fragment. It provides the external + /// address used for resource identification and linking (e.g., "http://example.com/users/123"). /// - public partial class UriResource + public partial class UriEndpoint : IUri { /// /// A regular expression to match URIs. @@ -37,23 +41,25 @@ public partial class UriResource /// /// The path (e.g. /over/there). /// - public ICollection PathSegments { get; } = []; + public IEnumerable PathSegments { get; private set; } = []; /// - /// Returns the extended path. The extended path is the postfix of the resource's path. + /// Returns the extended segment of the endpoint's path, which is included only when the endpoint class has the IncludeSubPaths attribute enabled. + /// For example, if the core endpoint is "http://example.com/server/app/endpoint" and the extended segment is "extended", + /// the complete URI becomes "http://example.com/server/app/endpoint/extended". In this case, the property returns the "extended" part. /// - public UriResource ExtendedPath + public IUri ExtendedPath { get { - return new UriResource(Skip(EndpointRoot.PathSegments.Count).PathSegments?.ToArray()); + return new UriEndpoint(Skip(EndpointRoot.PathSegments.Count()).PathSegments?.ToArray()); } } /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// - public ICollection Query { get; } = []; + public IEnumerable Query { get; } = []; /// /// References a position within a resource (e.g. #Anchor). @@ -87,27 +93,31 @@ public virtual string Display /// /// Determines if the uri is empty. /// - public bool Empty => PathSegments.Count == 0; + public bool Empty => !PathSegments.Any(); /// - /// Returns the root of the endpoint. + /// Retrieves the base URI of the endpoint. When the IncludeSubPaths attribute is enabled on the endpoint class, + /// the complete URI may include extra path segments. For example, the core endpoint could be + /// "http://example.com/server/app/endpoint", but with IncludeSubPaths enabled, the full URI might become + /// "http://example.com/server/app/endpoint/extended". In such cases, this property returns only the base URI: + /// "http://example.com/server/app/endpoint". /// - public virtual UriResource EndpointRoot { get; set; } + public virtual IUri EndpointRoot { get; set; } /// /// Returns the root of the application. /// - public virtual UriResource ApplicationRoot { get; set; } + public virtual IUri ApplicationRoot { get; set; } /// /// Returns the root of the server. /// - public virtual UriResource ServerRoot { get; set; } + public virtual IUri ServerRoot { get; set; } /// /// Determines if the Uri is the root. /// - public bool IsRoot => PathSegments.Count == 1; + public bool IsRoot => PathSegments.Count() == 1; /// /// Checks if it is a relative uri. @@ -115,9 +125,9 @@ public virtual string Display public bool IsRelative => Authority == null; /// - /// Returns the variables. + /// Retrieves a collection of variables represented as key-value pairs. /// - public Dictionary Parameters + public IDictionary Parameters { get { @@ -141,7 +151,7 @@ public Dictionary Parameters /// /// Initializes a new instance of the class. /// - public UriResource() + public UriEndpoint() { } @@ -152,7 +162,7 @@ public UriResource() /// The scheme (e.g. Http, FTP). /// The authority (e.g. user@example.com:8080). /// The uri. - public UriResource(UriScheme scheme, UriAuthority authority, string uri) + public UriEndpoint(UriScheme scheme, UriAuthority authority, string uri) : this(uri) { Scheme = scheme; @@ -163,7 +173,7 @@ public UriResource(UriScheme scheme, UriAuthority authority, string uri) /// Initializes a new instance of the class. /// /// The uri. - public UriResource(string uri) + public UriEndpoint(string uri) { if (string.IsNullOrWhiteSpace(uri) || uri == "/") return; @@ -193,11 +203,11 @@ public UriResource(string uri) var relativeMatch = RelativeUriRegex().Match(uri); - PathSegments.Add(new UriPathSegmentRoot()); + PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); foreach (var p in relativeMatch.Groups[2].Value.Split('/', StringSplitOptions.RemoveEmptyEntries)) { - PathSegments.Add(new UriPathSegmentConstant(p)); + PathSegments = PathSegments.Concat([new UriPathSegmentConstant(p)]); } Fragment = relativeMatch.Groups[4].Success ? relativeMatch.Groups[4].Value : null; @@ -206,7 +216,7 @@ public UriResource(string uri) { var item = q.Split('='); - Query.Add(new UriQuerry(item[0], item.Length > 1 ? item[1] : null)); + Query = Query.Concat([new UriQuerry(item[0], item.Length > 1 ? item[1] : null)]); } } @@ -214,12 +224,12 @@ public UriResource(string uri) /// Copy constructor /// /// The uri. - public UriResource(UriResource uri) + public UriEndpoint(IUri uri) { Scheme = uri?.Scheme ?? UriScheme.Http; Authority = uri?.Authority; - PathSegments = uri?.PathSegments.Select(x => x.Copy()).ToList() ?? []; - Query = uri?.Query.Select(x => new UriQuerry(x.Key, x.Value)).ToList() ?? []; + PathSegments = uri?.PathSegments.Select(x => x.Copy()) ?? []; + Query = uri?.Query.Select(x => new UriQuerry(x.Key, x.Value)) ?? []; Fragment = uri?.Fragment; ServerRoot = uri?.ServerRoot; ApplicationRoot = uri?.ApplicationRoot; @@ -230,13 +240,13 @@ public UriResource(UriResource uri) /// Initializes a new instance of the class. /// /// The path segments. - public UriResource(params IUriPathSegment[] segments) + public UriEndpoint(params IUriPathSegment[] segments) { - PathSegments.Add(new UriPathSegmentRoot()); + PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); foreach (var segment in segments.Where(x => x is not UriPathSegmentRoot)) { - PathSegments.Add(segment); + PathSegments = PathSegments.Concat([segment]); } } @@ -245,7 +255,7 @@ public UriResource(params IUriPathSegment[] segments) /// /// The uri. /// The path segments. - public UriResource(UriResource uri, IEnumerable segments) + public UriEndpoint(IUri uri, IEnumerable segments) : this(uri.Scheme, uri.Authority, uri.Fragment, uri.Query, segments) { ServerRoot = uri.ServerRoot; @@ -259,12 +269,11 @@ public UriResource(UriResource uri, IEnumerable segments) /// The uri. /// The path segments. /// Other segments. - public UriResource(UriResource uri, IEnumerable segments, IEnumerable extendedSegments) + public UriEndpoint(IUri uri, IEnumerable segments, IEnumerable extendedSegments) : this(uri.Scheme, uri.Authority, uri.Fragment, uri.Query, extendedSegments != null ? segments.Union(extendedSegments) : segments) { ServerRoot = uri.ServerRoot; ApplicationRoot = uri.ApplicationRoot; - EndpointRoot = uri.EndpointRoot; } /// @@ -275,58 +284,51 @@ public UriResource(UriResource uri, IEnumerable segments, IEnum /// References a position within a resource (e.g. #Anchor). /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// The path segments. - public UriResource(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) + public UriEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) { Scheme = scheme; Authority = authority; - PathSegments.Add(new UriPathSegmentRoot()); - - foreach (var segment in segments != null ? segments.Where(x => x is not UriPathSegmentRoot) : []) - { - PathSegments.Add(segment.Copy()); - } - - Query = query.Select(x => new UriQuerry(x.Key, x.Value)).ToList(); + PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); + PathSegments = PathSegments.Concat(segments?.Where(x => x is not UriPathSegmentRoot).Select(x => x.Copy()) ?? []); + Query = query.Select(x => new UriQuerry(x.Key, x.Value)); Fragment = fragment; } /// - /// Adds a path element. + /// Concatenates the given path segment to the current URI and returns a new instance of IUri with the updated path. /// - /// The path to append. - /// The extended path. - public virtual UriResource Append(string path) + /// The path segment to be concatenated with the existing URI. + /// A new IUri instance representing the URI after concatenation. + public virtual IUri Concat(string segment) { - if (string.IsNullOrWhiteSpace(path)) + if (string.IsNullOrWhiteSpace(segment)) { return this; } - var copy = new UriResource(this); - - foreach (var p in path.Split('/', StringSplitOptions.RemoveEmptyEntries)) - { - copy.PathSegments.Add(new UriPathSegmentConstant(p)); - } + var copy = new UriEndpoint((IUri)this); + copy.PathSegments = copy.PathSegments + .Concat(segment.Split('/', StringSplitOptions.RemoveEmptyEntries) + .Select(x => new UriPathSegmentConstant(x))); return copy; } /// - /// Adds a path element. + /// Concatenates the given path segment to the current URI and returns a new instance of IUri with the updated path. /// - /// The path to append. - /// The extended path. - public virtual UriResource Append(IUriPathSegment path) + /// An array of path segments to be concatenated to the existing URI. + /// A new IUri instance representing the URI after concatenation. + public virtual IUri Concat(params IUriPathSegment[] segments) { - if (path == null || path.IsEmpty) + if (segments.Length == 0) { return this; } - var copy = new UriResource(this); - - copy.PathSegments.Add(path); + var copy = new UriEndpoint((IUri)this); + copy.PathSegments = copy.PathSegments + .Select(x => x.Copy()); return copy; } @@ -339,23 +341,23 @@ public virtual UriResource Append(IUriPathSegment path) /// /// The count of elements to include or truncate. /// The sub uri with the specified number of elements. - public virtual UriResource Take(int count) + public virtual IUri Take(int count) { - var copy = new UriResource(this); + var copy = new UriEndpoint((IUri)this); var path = copy.PathSegments.ToList(); - copy.PathSegments.Clear(); + copy.PathSegments = []; if (count == 0) { - return new UriResource(); + return new UriEndpoint(); } else if (count > 0) { - (copy.PathSegments as List).AddRange(path.Take(count)); + copy.PathSegments = copy.PathSegments.Concat(path.Take(count)); } else if (count < 0 && Math.Abs(count) < path.Count) { - (copy.PathSegments as List).AddRange(path.Take(path.Count + count)); + copy.PathSegments = copy.PathSegments.Concat(path.Take(path.Count + count)); } else { @@ -372,23 +374,23 @@ public virtual UriResource Take(int count) /// /// The count of elements to skip. /// The sub uri after skipping the specified number of elements. - public UriResource Skip(int count) + public IUri Skip(int count) { - if (count >= PathSegments.Count) + if (count >= PathSegments.Count()) { return null; } if (count > 0) { - var copy = new UriResource(this); + var copy = new UriEndpoint((IUri)this); var path = copy.PathSegments.ToList(); - copy.PathSegments.Clear(); - (copy.PathSegments as List).AddRange(path.Skip(count)); + copy.PathSegments = []; + copy.PathSegments = copy.PathSegments.Concat(path.Skip(count)); return copy; } - return new UriResource(this); + return new UriEndpoint((IUri)this); } /// @@ -406,17 +408,17 @@ public virtual bool Contains(string segment) /// /// The Uri to be checked. /// true if part of the uri, false otherwise. - public bool StartsWith(UriResource uri) + public bool StartsWith(IUri uri) { return ToString().StartsWith(uri.ToString()); } /// - /// Creates a new resource uri and fills it with the given parameters. + /// Creates a new endpoint uri and fills it with the given parameters. /// /// The parameters that fill in the variable parts of the uri. - /// A new resource uri with the populated parameters. - public UriResource SetParameters(params WebMessage.Parameter[] parameters) + /// A new endpoint uri with the populated parameters. + public IUri SetParameters(params WebMessage.Parameter[] parameters) { var pathSegments = PathSegments.AsEnumerable(); @@ -437,49 +439,7 @@ public UriResource SetParameters(params WebMessage.Parameter[] parameters) }); } - return new UriResource(this, pathSegments); - } - - /// - /// Converts the uri to a string. - /// - /// A string that represents the current uri. - public override string ToString() - { - var defaultPort = Scheme switch - { - UriScheme.Http => 80, - UriScheme.Https => 443, - UriScheme.FTP => 21, - UriScheme.Ldap => 389, - UriScheme.Ldaps => 636, - _ => -1 - - }; - - var scheme = Scheme.ToString("g").ToLower() + ":"; - var authority = Authority?.ToString(defaultPort); - var uri = "/" + string.Join - ( - "/", - PathSegments.Where(x => x is not UriPathSegmentRoot).Select(x => x.ToString()) - ); - - if (!string.IsNullOrWhiteSpace(Fragment)) - { - uri += "#" + Fragment; - } - - if (Query.Count != 0) - { - uri += "?" + string.Join("&", Query.Select(x => $"{x.Key}={x.Value}")); - } - - return Scheme switch - { - UriScheme.Mailto => string.Format("{0}{1}", scheme, authority), - _ => IsRelative ? uri : string.Format("{0}{1}{2}", scheme, authority, uri), - }; + return new UriEndpoint(this, pathSegments); } /// @@ -487,15 +447,16 @@ public override string ToString() /// /// The uris to be combine. /// A combined uri. - public static UriResource Combine(params string[] uris) + public static IUri Combine(params string[] uris) { - var uri = new UriResource(); + var copy = new UriEndpoint(); - (uri.PathSegments as List).AddRange(uris.Where(x => !string.IsNullOrWhiteSpace(x)) - .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) - .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); + copy.PathSegments = copy.PathSegments + .Concat(uris.Where(x => !string.IsNullOrWhiteSpace(x)) + .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) + .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); - return uri; + return copy; } /// @@ -503,12 +464,13 @@ public static UriResource Combine(params string[] uris) /// /// The uris to be combine. /// A combined uri. - public static UriResource Combine(params UriResource[] uris) + public static IUri Combine(params IUri[] uris) { - var uri = new UriResource(uris.FirstOrDefault()); - (uri.PathSegments as List).AddRange(uris.Skip(1).SelectMany(x => x.PathSegments.Skip(1))); + var copy = new UriEndpoint(uris.FirstOrDefault()); + copy.PathSegments = copy.PathSegments + .Concat(uris.Skip(1).SelectMany(x => x.PathSegments.Skip(1))); - return uri; + return copy; } /// @@ -517,12 +479,14 @@ public static UriResource Combine(params UriResource[] uris) /// The first uri to be combine. /// The uris to be combine. /// A combined uri. - public static UriResource Combine(UriResource uri, params string[] uris) + public static IUri Combine(IUri uri, params string[] uris) { - var copy = new UriResource(uri); - (copy.PathSegments as List).AddRange(uris.Where(x => !string.IsNullOrWhiteSpace(x)) - .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) - .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); + var copy = new UriEndpoint(uri); + copy.PathSegments = copy.PathSegments + .Concat(uris.Where(x => !string.IsNullOrWhiteSpace(x)) + .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) + .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); + return copy; } @@ -530,11 +494,52 @@ public static UriResource Combine(UriResource uri, params string[] uris) /// Converts a resource uri to a normal uri. /// /// The uri to convert. - public static implicit operator string(UriResource uri) + public static implicit operator string(UriEndpoint uri) { return uri?.ToString(); } + /// + /// Converts the uri to a string. + /// + /// A string that represents the current uri. + public override string ToString() + { + var defaultPort = Scheme switch + { + UriScheme.Http => 80, + UriScheme.Https => 443, + UriScheme.FTP => 21, + UriScheme.Ldap => 389, + UriScheme.Ldaps => 636, + _ => -1 + + }; + + var scheme = Scheme.ToString("g").ToLower() + ":"; + var authority = Authority?.ToString(defaultPort); + var uri = "/" + string.Join + ( + "/", + PathSegments.Where(x => x is not UriPathSegmentRoot) + .Select(x => x.ToString().TrimStart('/')) + ).TrimEnd('/'); + + if (!string.IsNullOrWhiteSpace(Fragment)) + { + uri += "#" + Fragment; + } + + if (Query.Any()) + { + uri += "?" + string.Join("&", Query.Select(x => $"{x.Key}={x.Value}")); + } + return Scheme switch + { + UriScheme.Mailto => string.Format("{0}{1}", scheme, authority), + _ => IsRelative ? uri : string.Format("{0}{1}{2}", scheme, authority, uri), + }; + } } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebUri/UriFragment.cs b/src/WebExpress.WebCore/WebUri/UriFragment.cs index 76df232..fb4afd4 100644 --- a/src/WebExpress.WebCore/WebUri/UriFragment.cs +++ b/src/WebExpress.WebCore/WebUri/UriFragment.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebUri /// /// Uri which consists only of the fragment (e.g. #). /// - public class UriFragment : UriResource + public class UriFragment : UriEndpoint { /// /// Initializes a new instance of the class. From 4fc3fbec8a78372888e836e39065f8d548c1d07a Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 29 Mar 2025 10:12:17 +0100 Subject: [PATCH 145/162] fix: update URI handling according to RFC 3986 --- .../Route/UnitTestRoute.cs | 56 +---- .../Uri/UnitTestUri.cs | 213 +++++++++++------- .../Uri/UnitTestUriAbsolute.cs | 133 ----------- .../Uri/UnitTestUriConcat.cs | 31 --- .../Uri/UnitTestUriRelativeExtendedPath.cs | 44 ---- .../Uri/UnitTestUriSkip.cs | 33 --- .../Uri/UnitTestUriTake.cs | 38 ---- .../WebAsset/AssetContext.cs | 2 +- .../WebEndpoint/RouteEndpoint.cs | 31 +-- src/WebExpress.WebCore/WebUri/IUri.cs | 2 +- src/WebExpress.WebCore/WebUri/UriEndpoint.cs | 36 ++- .../WebUri/{UriQuerry.cs => UriQuery.cs} | 13 +- src/WebExpress.WebCore/WebUri/UriScheme.cs | 28 ++- 13 files changed, 203 insertions(+), 457 deletions(-) delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs delete mode 100644 src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs rename src/WebExpress.WebCore/WebUri/{UriQuerry.cs => UriQuery.cs} (66%) diff --git a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs index f29a458..2980959 100644 --- a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs +++ b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs @@ -1,5 +1,4 @@ using WebExpress.WebCore.WebEndpoint; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Route { @@ -33,66 +32,31 @@ public void Concat(string baseRoute, string segment, string expected, int count) /// Test the combine method. /// [Theory] - [InlineData("/a/b/c", null, "/a/b/c", 4)] - [InlineData("/a/b/c", "", "/a/b/c", 4)] - [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] - [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] - public void CombinePath(string baseRoute, string pathB, string expected, int count) + [InlineData("/a/b/c", null, "/a/b/c")] + [InlineData("/a/b/c", "", "/a/b/c")] + [InlineData("/a/b/c", "d", "/a/b/c/d")] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f")] + public void CombinePath(string baseRoute, string pathB, string expected) { // test execution var combine = RouteEndpoint.Combine([new RouteEndpoint(baseRoute), new RouteEndpoint(pathB)]); Assert.Equal(expected, combine.ToString()); - Assert.Equal(count, combine.PathSegments.Count()); } /// /// Test the combine method. /// [Theory] - [InlineData("/a/b/c", null, "/a/b/c", 4)] - [InlineData("/a/b/c", "", "/a/b/c", 4)] - [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] - [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] - public void CombineRoute(string baseRoute, string pathB, string expected, int count) + [InlineData("/a/b/c", null, "/a/b/c")] + [InlineData("/a/b/c", "", "/a/b/c")] + [InlineData("/a/b/c", "d", "/a/b/c/d")] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f")] + public void CombineRoute(string baseRoute, string pathB, string expected) { // test execution var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), [pathB]); - Assert.Equal(expected, combine.ToString()); - Assert.Equal(count, combine.PathSegments.Count()); - } - - /// - /// Test the combine method. - /// - [Theory] - [InlineData("/a/b/c", "", null, "/a/b/c")] - [InlineData("/a/b/c", "", "", "/a/b/c")] - [InlineData("/a/b/c", "", "d", "/a/b/c/d")] - [InlineData("/a/b/c", "", "/d/e/f", "/a/b/c/d/e/f")] - public void CombineSegment(string baseRoute, string intermediateRoute, string segment, string expected) - { - // test execution - var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), new RouteEndpoint(intermediateRoute), new UriPathSegmentConstant(segment)); - - Assert.Equal(expected, combine.ToString()); - } - - /// - /// Test the combine method. - /// - [Theory] - [InlineData("/a/b/c", "", null, "/a/b/c")] - [InlineData("/a/b/c", "", new[] { "" }, "/a/b/c")] - [InlineData("/a/b/c", "", new[] { "d" }, "/a/b/c/d")] - [InlineData("/a/b/c", "", new[] { "d", "e", "f" }, "/a/b/c/d/e/f")] - public void CombineSegments(string baseRoute, string intermediateRoute, string[] segments, string expected) - { - // test execution - var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), new RouteEndpoint(intermediateRoute), segments - ?.Select(x => new UriPathSegmentConstant(x)) ?? []); - Assert.Equal(expected, combine.ToString()); } } diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs index 04e8c1b..b6886e4 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs @@ -8,97 +8,154 @@ namespace WebExpress.WebCore.Test.Uri [Collection("NonParallelTests")] public class UnitTestUri { - [Fact] - public void Test_0() + /// + /// Test the constructor with absolute URIs. + /// + [Theory] + [InlineData(UriScheme.Http, "www.example.com", null, null, null, "a=1&b=2", "fragment", "http://www.example.com/?a=1&b=2#fragment")] + [InlineData(UriScheme.Http, "www.example.com", null, null, "", "a=1&b=2", "fragment", "http://www.example.com/?a=1&b=2#fragment")] + [InlineData(UriScheme.Http, "www.example.com", null, null, "/abc", "a=1&b=2", "fragment", "http://www.example.com/abc?a=1&b=2#fragment")] + [InlineData(UriScheme.Http, "example.com", "user", "8080", "/abc", "a=1&b=2", "fragment", "http://user@example.com:8080/abc?a=1&b=2#fragment")] + [InlineData(UriScheme.Http, "example", null, null, "/assets/img/example.svg", null, null, "http://example/assets/img/example.svg")] + [InlineData(UriScheme.Http, "localhost", null, null, null, null, null, "http://localhost/")] + [InlineData(UriScheme.Http, "localhost", null, null, "/", null, null, "http://localhost/")] + [InlineData(UriScheme.Http, "example.com", "user", "80", "/abc", "a=1&b=2", "fragment", "http://user@example.com/abc?a=1&b=2#fragment")] + public void UriAbsolute(UriScheme scheme, string authority, string user, string port, string path, string query, string fragment, string expected) { - var str = "/abc#a?b=1&c=2"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.PathSegments.Count() == 2 && - uri.Fragment == "a" && - uri.Query.FirstOrDefault()?.Key == "b" && - uri.Query.FirstOrDefault()?.Value == "1" && - uri.Query.LastOrDefault()?.Key == "c" && - uri.Query.LastOrDefault()?.Value == "2" && - uri.IsRelative - ); + // preconditions + var uriUser = user != null ? user + "@" : ""; + var uriPort = port != null ? ":" + port : null; + var uriQuery = query != null ? "?" + query : ""; + var uriFragment = fragment != null ? "#" + fragment : null; + + // test execution + var uri = new UriEndpoint($"{scheme}://{uriUser}{authority}{uriPort}{path}{uriQuery}{uriFragment}"); + + Assert.Equal(expected, uri.ToString()); + Assert.Equal(scheme, uri.Scheme); + Assert.Equal(authority, uri.Authority.Host); + Assert.Equal(path, !string.IsNullOrWhiteSpace(path) + ? "/" + string.Join("/", uri.PathSegments.Skip(1)) + : path); + Assert.Equal(query, uri.Query.Any() ? string.Join("&", uri.Query) : null); + Assert.Equal(fragment, uri.Fragment); + } + + /// + /// Test the constructor with relative URIs. + /// + [Theory] + [InlineData(null, null, null, "/")] + [InlineData(null, "a=1&b=2", null, "/?a=1&b=2")] + [InlineData(null, null, "fragment", "/#fragment")] + [InlineData(null, "a=1&b=2", "fragment", "/?a=1&b=2#fragment")] + [InlineData("", null, null, "/")] + [InlineData("", "a=1&b=2", null, "/?a=1&b=2")] + [InlineData("", null, "fragment", "/#fragment")] + [InlineData("", "a=1&b=2", "fragment", "/?a=1&b=2#fragment")] + [InlineData("/", null, null, "/")] + [InlineData("/", "a=1&b=2", null, "/?a=1&b=2")] + [InlineData("/abc", "a=1&b=2", "fragment", "/abc?a=1&b=2#fragment")] + [InlineData("/assets/img/example.svg", null, null, "/assets/img/example.svg")] + public void UriRelative(string path, string query, string fragment, string expected) + { + // preconditions + var uriQuery = query != null ? "?" + query : ""; + var uriFragment = fragment != null ? "#" + fragment : null; + + // test execution + var uri = new UriEndpoint($"{path}{uriQuery}{uriFragment}"); + + Assert.Equal(expected, uri.ToString()); + Assert.Equal(path, !string.IsNullOrWhiteSpace(path) + ? "/" + string.Join("/", uri.PathSegments.Skip(1)) + : path); + Assert.Equal(query, uri.Query.Any() ? string.Join("&", uri.Query) : null); + Assert.Equal(fragment, uri.Fragment); } - [Fact] - public void Test_1() + /// + /// Test the concat method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", "", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void Concat(string path, string segment, string expected, int count) { - var str = "/assets/img/vila.svg"; - var uri = new UriEndpoint("/assets/img/vila.svg"); - - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.PathSegments.Count() == 4 && - uri.Fragment == null && - uri.Query.Any() == false && - uri.IsRelative - ); + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var concat = uri.Concat(segment); + + Assert.Equal(expected, concat.ToString()); + Assert.Equal(count, concat.PathSegments.Count()); } - [Fact] - public void Test_2() + /// + /// Test the skip method. + /// + [Theory] + [InlineData("/a/b/c", 0, "/a/b/c")] + [InlineData("/a/b/c", 1, "/a/b/c")] + [InlineData("/a/b/c", 2, "/b/c")] + [InlineData("/a/b/c", 3, "/c")] + [InlineData("/a/b/c", 4, null)] + [InlineData("/a/b/c", 5, null)] + public void Skip(string path, int skipCount, string expected) { - var str = "/"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.Authority == null && - uri.PathSegments.Count() == 0 && - uri.Fragment == null && - uri.Query.Any() == false && - uri.IsRelative - ); + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var skip = uri.Skip(skipCount); + + Assert.Equal(expected, skip?.ToString()); } - [Fact] - public void Test_3() + /// + /// Test the take method. + /// + [Theory] + [InlineData("/a/b/c", 0, "/")] + [InlineData("/a/b/c", 1, "/")] + [InlineData("/a/b/c", 2, "/a")] + [InlineData("/a/b/c", 3, "/a/b")] + [InlineData("/a/b/c", 4, "/a/b/c")] + [InlineData("/a/b/c", 5, "/a/b/c")] + [InlineData("/a/b/c", -1, "/a/b")] + [InlineData("/a/b/c", -2, "/a")] + [InlineData("/a/b/c", -3, "/")] + [InlineData("/a/b/c", -4, null)] + [InlineData("/a/b/c", -5, null)] + public void Take(string path, int takeCount, string expected) { - var str = "/?b=1&c=2"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.Authority == null && - uri.PathSegments.Count() == 1 && - uri.Query.FirstOrDefault()?.Key == "b" && - uri.Query.FirstOrDefault()?.Value == "1" && - uri.Query.LastOrDefault()?.Key == "c" && - uri.Query.LastOrDefault()?.Value == "2" && - uri.IsRelative - ); + // preconditions + var uri = new UriEndpoint(path); + + // test execution + var take = uri.Take(takeCount); + + Assert.Equal(expected, take?.ToString()); } - [Fact] - public void Test_4() + /// + /// Test the extended path property. + /// + [Theory] + [InlineData("http://user@example.com/x/y/z", "http://user@example.com/", "/x/y/z")] + [InlineData("http://user@example.com/a/b/c/x/y/z", "http://user@example.com/a/b/c", "/x/y/z")] + public void ExtendedPath(string uri, string endpointUri, string expected) { - var str = ""; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str + "/" && - uri.Scheme == UriScheme.Http && - uri.Authority == null && - uri.PathSegments.Count() == 0 && - uri.Fragment == null && - uri.Query.Any() == false && - uri.IsRelative - ); + var resourceUri = new UriEndpoint(uri) + { + EndpointRoot = new UriEndpoint(endpointUri) + }; + + Assert.Equal(uri, resourceUri.ToString()); + Assert.Equal(expected, resourceUri.ExtendedPath.ToString()); } } } diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs deleted file mode 100644 index fcb8da2..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriAbsolute.cs +++ /dev/null @@ -1,133 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests an absolute Uri. - /// - [Collection("NonParallelTests")] - public class UnitTestUriAbsolute - { - [Fact] - public void Test_0() - { - var str = "http://user@example.com:8080/abc#a?b=1&c=2"; - var uri = new UriEndpoint(str); - - Assert.Equal(uri.ToString(), str); - Assert.Equal(UriScheme.Http, uri.Scheme); - Assert.Equal("user", uri.Authority.User); - Assert.Equal("example.com", uri.Authority.Host); - Assert.Equal(8080, uri.Authority.Port); - Assert.Equal("a", uri.Fragment); - Assert.Equal("b", uri.Query.FirstOrDefault()?.Key); - Assert.Equal("1", uri.Query.FirstOrDefault()?.Value); - Assert.Equal("c", uri.Query.LastOrDefault()?.Key); - Assert.Equal("2", uri.Query.LastOrDefault()?.Value); - Assert.False(uri.IsRelative); - } - - [Fact] - public void Test_1() - { - var str = "http://vila/assets/img/vila.svg"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str && - uri.Scheme == UriScheme.Http && - uri.Authority.User == null && - uri.Authority.Host == "vila" && - uri.Authority.Port == null && - uri.Fragment == null && - uri.Query.Any() == false && - uri.IsRelative == false - ); - } - - [Fact] - public void Test_2() - { - var str = "http://localhost"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == str + "/" && - uri.Scheme == UriScheme.Http && - uri.Authority.User == null && - uri.Authority.Host == "localhost" && - uri.Authority.Port == null && - uri.Fragment == null && - uri.Query.Any() == false && - uri.IsRelative == false - ); - } - - [Fact] - public void Test_3() - { - var str = "http://user@example.com:80/abc#a?b=1&c=2"; - var uri = new UriEndpoint(str); - - Assert.True - ( - uri.ToString() == "http://user@example.com/abc#a?b=1&c=2" && - uri.Scheme == UriScheme.Http && - uri.Authority.User == "user" && - uri.Authority.Host == "example.com" && - uri.Authority.Port == 80 && - uri.Fragment == "a" && - uri.Query.FirstOrDefault()?.Key == "b" && - uri.Query.FirstOrDefault()?.Value == "1" && - uri.Query.LastOrDefault()?.Key == "c" && - uri.Query.LastOrDefault()?.Value == "2" && - uri.IsRelative == false - ); - } - - [Fact] - public void Test_4() - { - var str = "http://user@example.com:80/abc#a?b=1&c=2"; - var uri = new UriEndpoint(str); - var segments = new List - { - new UriPathSegmentRoot(), - new UriPathSegmentConstant("a"), - new UriPathSegmentConstant("b"), - new UriPathSegmentConstant("c") - }; - - var extendetSegments = new List - { - new UriPathSegmentRoot(), - new UriPathSegmentConstant("x"), - new UriPathSegmentConstant("y") - }; - - var resourceUri = new UriEndpoint(uri, segments); - resourceUri = new UriEndpoint(resourceUri, resourceUri.PathSegments, extendetSegments); - resourceUri.ServerRoot = new UriEndpoint("http://user@example.com:80"); - resourceUri.ApplicationRoot = new UriEndpoint("http://user@example.com:80"); - resourceUri.EndpointRoot = new UriEndpoint("http://user@example.com:80/abc"); - - Assert.True - ( - resourceUri.ToString() == "http://user@example.com/a/b/c/x/y#a?b=1&c=2" && - resourceUri.Scheme == UriScheme.Http && - resourceUri.Authority.User == "user" && - resourceUri.Authority.Host == "example.com" && - resourceUri.Authority.Port == 80 && - resourceUri.Fragment == "a" && - resourceUri.Query.FirstOrDefault()?.Key == "b" && - resourceUri.Query.FirstOrDefault()?.Value == "1" && - resourceUri.Query.LastOrDefault()?.Key == "c" && - resourceUri.Query.LastOrDefault()?.Value == "2" && - resourceUri.ServerRoot != null && - resourceUri.IsRelative == false - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs deleted file mode 100644 index c124817..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriConcat.cs +++ /dev/null @@ -1,31 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the concat method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriConcat - { - /// - /// Test the concat method. - /// - [Theory] - [InlineData("/a/b/c", null, "/a/b/c", 4)] - [InlineData("/a/b/c", "", "/a/b/c", 4)] - [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] - [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] - public void Concat(string path, string segment, string expected, int count) - { - // preconditions - var uri = new UriEndpoint(path); - - // test execution - var concat = uri.Concat(segment); - - Assert.Equal(expected, concat.ToString()); - Assert.Equal(count, concat.PathSegments.Count()); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs deleted file mode 100644 index 3e48d03..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriRelativeExtendedPath.cs +++ /dev/null @@ -1,44 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the extended path property. - /// - [Collection("NonParallelTests")] - public class UnitTestUriRelativeExtendedPath - { - private readonly UriEndpoint Uri = new UriEndpoint("http://user@example.com:80"); - - [Fact] - public void ExtendedPath_0() - { - var segments = new List - { - new UriPathSegmentRoot(), - new UriPathSegmentConstant("a"), - new UriPathSegmentConstant("b"), - new UriPathSegmentConstant("c"), - new UriPathSegmentConstant("x"), - new UriPathSegmentConstant("y") - }; - - var resourceUri = new UriEndpoint(Uri, segments); - resourceUri.ServerRoot = new UriEndpoint("http://user@example.com:80"); - resourceUri.ApplicationRoot = new UriEndpoint("http://user@example.com:80"); - resourceUri.EndpointRoot = new UriEndpoint("http://user@example.com:80/a/b/c"); - - Assert.True - ( - resourceUri.ToString() == "http://user@example.com/a/b/c/x/y" && - resourceUri.ExtendedPath.ToString() == "/x/y" && - resourceUri.Scheme == UriScheme.Http && - resourceUri.Authority.User == "user" && - resourceUri.Authority.Host == "example.com" && - resourceUri.Authority.Port == 80 && - resourceUri.ServerRoot != null && - resourceUri.IsRelative == false - ); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs deleted file mode 100644 index c836099..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriSkip.cs +++ /dev/null @@ -1,33 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the skip method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriSkip - { - /// - /// Test the skip method. - /// - [Theory] - [InlineData("/a/b/c", 0, "/a/b/c", 4)] - [InlineData("/a/b/c", 1, "/a/b/c", 3)] - [InlineData("/a/b/c", 2, "/b/c", 2)] - [InlineData("/a/b/c", 3, "/c", 1)] - [InlineData("/a/b/c", 4, null, 0)] - [InlineData("/a/b/c", 5, null, 0)] - public void Skip(string path, int skipCount, string expected, int count) - { - // preconditions - var uri = new UriEndpoint(path); - - // test execution - var skip = uri.Skip(skipCount); - - Assert.Equal(expected, skip?.ToString()); - Assert.Equal(count, skip?.PathSegments.Count() ?? 0); - } - } -} diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs deleted file mode 100644 index a16f038..0000000 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUriTake.cs +++ /dev/null @@ -1,38 +0,0 @@ -using WebExpress.WebCore.WebUri; - -namespace WebExpress.WebCore.Test.Uri -{ - /// - /// Tests the take method. - /// - [Collection("NonParallelTests")] - public class UnitTestUriTake - { - /// - /// Test the take method. - /// - [Theory] - [InlineData("/a/b/c", 0, "/", 0)] - [InlineData("/a/b/c", 1, "/", 1)] - [InlineData("/a/b/c", 2, "/a", 2)] - [InlineData("/a/b/c", 3, "/a/b", 3)] - [InlineData("/a/b/c", 4, "/a/b/c", 4)] - [InlineData("/a/b/c", 5, "/a/b/c", 4)] - [InlineData("/a/b/c", -1, "/a/b", 3)] - [InlineData("/a/b/c", -2, "/a", 2)] - [InlineData("/a/b/c", -3, "/", 1)] - [InlineData("/a/b/c", -4, null, 0)] - [InlineData("/a/b/c", -5, null, 0)] - public void Take(string path, int takeCount, string expected, int count) - { - // preconditions - var uri = new UriEndpoint(path); - - // test execution - var take = uri.Take(takeCount); - - Assert.Equal(expected, take?.ToString()); - Assert.Equal(count, take?.PathSegments.Count() ?? 0); - } - } -} diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs index 7be77fd..616dfbe 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetContext.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -50,7 +50,7 @@ public class AssetContext : IAssetContext /// /// Returns the internal routing path for the endpoint. /// - public IRoute Route => RouteEndpoint.Combine(ApplicationContext.ContextPath, _contextPath, _pathSegment); + public IRoute Route => RouteEndpoint.Combine(ApplicationContext.ContextPath, _contextPath.Concat(_pathSegment)); /// /// Returns the attributes associated with the page. diff --git a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs index bff8ba0..27cc094 100644 --- a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs +++ b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs @@ -121,7 +121,7 @@ public RouteEndpoint(IRoute route, IEnumerable segments, IEnume /// References a position within a resource (e.g. #Anchor). /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// The path segments. - public RouteEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) + public RouteEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) { } @@ -212,35 +212,6 @@ public static IRoute Combine(IRoute baseRoute, IEnumerable segm return copy; } - /// - /// Combines the specified base route, intermediate route, and enumerable segments into a compound route. - /// - /// The base route serving as the starting point. - /// The intermediate route to be appended, if not empty or whitespace. - /// The enumerable collection of path segments to be added. - /// A new compound route combining all specified components. - public static IRoute Combine(IRoute baseRoute, string intermediateRoute, IEnumerable segments) - { - var copy = new RouteEndpoint(baseRoute); - copy.PathSegments = copy.PathSegments - .Concat(!string.IsNullOrWhiteSpace(intermediateRoute) ? [new UriPathSegmentConstant(intermediateRoute)] : []) - .Concat(segments); - - return copy; - } - - /// - /// Combines the specified base route, intermediate route, and single segment into a compound route. - /// - /// The base route to be used as the starting point. - /// The intermediate route to be appended. - /// The individual segment to be added to the resulting route. - /// A newly combined route constructed from the provided components. - public static IRoute Combine(IRoute baseRoute, IRoute intermediateRoute, IUriPathSegment segment) - { - return Combine([baseRoute, intermediateRoute, new RouteEndpoint(segment)]); - } - /// /// Converts a resource uri to a normal uri. /// diff --git a/src/WebExpress.WebCore/WebUri/IUri.cs b/src/WebExpress.WebCore/WebUri/IUri.cs index 2fed8a5..edacef2 100644 --- a/src/WebExpress.WebCore/WebUri/IUri.cs +++ b/src/WebExpress.WebCore/WebUri/IUri.cs @@ -36,7 +36,7 @@ public interface IUri /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// - IEnumerable Query { get; } + IEnumerable Query { get; } /// /// References a position within a resource (e.g. #Anchor). diff --git a/src/WebExpress.WebCore/WebUri/UriEndpoint.cs b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs index f0236fc..a51079b 100644 --- a/src/WebExpress.WebCore/WebUri/UriEndpoint.cs +++ b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs @@ -6,8 +6,8 @@ namespace WebExpress.WebCore.WebUri { /// - /// An Uri represents a complete, fully qualified Uniform Resource Identifier (URI) that uniquely identifies a endpoint. - /// + /// An Uri represents a complete, fully qualified Uniform Resource Identifier (URI) that uniquely + /// identifies a endpoint (see RFC 3986). /// This interface encapsulates all components of a typical URI, such as the scheme (e.g., "http", "https"), /// the authority (e.g., "example.com"), path segments, query parameters, and fragment. It provides the external /// address used for resource identification and linking (e.g., "http://example.com/users/123"). @@ -18,14 +18,14 @@ public partial class UriEndpoint : IUri /// A regular expression to match URIs. /// /// A Regex object for matching URIs. - [GeneratedRegex("^([a-z0-9+.-]+):(?://(?:((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(?::(\\d*))?(.*)?)$")] + [GeneratedRegex("^([a-zA-Z0-9+.-]+):(?://(?:((?:[a-zA-Z0-9-._~!$&'()*+,;=:]|%[0-9a-fA-F]{2})*)@)?((?:[a-zA-Z0-9-._~!$&'()*+,;=]|%[0-9a-fA-F]{2})*)(?::(\\d*))?(.*)?)$")] private static partial Regex UriRegex(); /// /// Regular expression to match relative URIs. /// /// A Regex object for matching relative URIs. - [GeneratedRegex(@"^(\/([a-zA-Z0-9-+*%()=._/$]*))(#([a-zA-Z0-9-+*%()=._/$]*))?(\?(.*))?$")] + [GeneratedRegex(@"^(\/([a-zA-Z0-9-+*%()=._/$]*))?(\?([a-zA-Z0-9-+*%()=._/$&]*))?(#([a-zA-Z0-9-+*%()=._/$]*))?$")] private static partial Regex RelativeUriRegex(); /// @@ -41,7 +41,7 @@ public partial class UriEndpoint : IUri /// /// The path (e.g. /over/there). /// - public IEnumerable PathSegments { get; private set; } = []; + public IEnumerable PathSegments { get; private set; } = [new UriPathSegmentRoot()]; /// /// Returns the extended segment of the endpoint's path, which is included only when the endpoint class has the IncludeSubPaths attribute enabled. @@ -59,7 +59,7 @@ public IUri ExtendedPath /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// - public IEnumerable Query { get; } = []; + public IEnumerable Query { get; } = []; /// /// References a position within a resource (e.g. #Anchor). @@ -203,21 +203,19 @@ public UriEndpoint(string uri) var relativeMatch = RelativeUriRegex().Match(uri); - PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); - foreach (var p in relativeMatch.Groups[2].Value.Split('/', StringSplitOptions.RemoveEmptyEntries)) { PathSegments = PathSegments.Concat([new UriPathSegmentConstant(p)]); } - Fragment = relativeMatch.Groups[4].Success ? relativeMatch.Groups[4].Value : null; - - foreach (var q in relativeMatch.Groups[6].Success ? relativeMatch.Groups[6].Value?.Split('&') : Enumerable.Empty()) + foreach (var q in relativeMatch.Groups[4].Success ? relativeMatch.Groups[4].Value?.Split('&') : []) { var item = q.Split('='); - Query = Query.Concat([new UriQuerry(item[0], item.Length > 1 ? item[1] : null)]); + Query = Query.Concat([new UriQuery(item[0], item.Length > 1 ? item[1] : null)]); } + + Fragment = relativeMatch.Groups[6].Success ? relativeMatch.Groups[6].Value : null; } /// @@ -229,7 +227,7 @@ public UriEndpoint(IUri uri) Scheme = uri?.Scheme ?? UriScheme.Http; Authority = uri?.Authority; PathSegments = uri?.PathSegments.Select(x => x.Copy()) ?? []; - Query = uri?.Query.Select(x => new UriQuerry(x.Key, x.Value)) ?? []; + Query = uri?.Query.Select(x => new UriQuery(x.Key, x.Value)) ?? []; Fragment = uri?.Fragment; ServerRoot = uri?.ServerRoot; ApplicationRoot = uri?.ApplicationRoot; @@ -284,13 +282,13 @@ public UriEndpoint(IUri uri, IEnumerable segments, IEnumerable< /// References a position within a resource (e.g. #Anchor). /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// The path segments. - public UriEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) + public UriEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IEnumerable query, IEnumerable segments) { Scheme = scheme; Authority = authority; PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); PathSegments = PathSegments.Concat(segments?.Where(x => x is not UriPathSegmentRoot).Select(x => x.Copy()) ?? []); - Query = query.Select(x => new UriQuerry(x.Key, x.Value)); + Query = query.Select(x => new UriQuery(x.Key, x.Value)); Fragment = fragment; } @@ -525,14 +523,14 @@ public override string ToString() .Select(x => x.ToString().TrimStart('/')) ).TrimEnd('/'); - if (!string.IsNullOrWhiteSpace(Fragment)) + if (Query.Any()) { - uri += "#" + Fragment; + uri += "?" + string.Join("&", Query.Select(x => x.ToString())); } - if (Query.Any()) + if (!string.IsNullOrWhiteSpace(Fragment)) { - uri += "?" + string.Join("&", Query.Select(x => $"{x.Key}={x.Value}")); + uri += "#" + Fragment; } return Scheme switch diff --git a/src/WebExpress.WebCore/WebUri/UriQuerry.cs b/src/WebExpress.WebCore/WebUri/UriQuery.cs similarity index 66% rename from src/WebExpress.WebCore/WebUri/UriQuerry.cs rename to src/WebExpress.WebCore/WebUri/UriQuery.cs index 8ed3979..54f6982 100644 --- a/src/WebExpress.WebCore/WebUri/UriQuerry.cs +++ b/src/WebExpress.WebCore/WebUri/UriQuery.cs @@ -3,7 +3,7 @@ namespace WebExpress.WebCore.WebUri /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). /// - public class UriQuerry + public class UriQuery { /// /// Returns the key. @@ -20,10 +20,19 @@ public class UriQuerry /// /// The key. /// The value. - public UriQuerry(string key, string value) + public UriQuery(string key, string value) { Key = key; Value = value; } + + /// + /// Converts the query to a string. + /// + /// A string that represents the current query. + public override string ToString() + { + return $"{Key}={Value}"; + } } } \ No newline at end of file diff --git a/src/WebExpress.WebCore/WebUri/UriScheme.cs b/src/WebExpress.WebCore/WebUri/UriScheme.cs index 7937edc..2b11f04 100644 --- a/src/WebExpress.WebCore/WebUri/UriScheme.cs +++ b/src/WebExpress.WebCore/WebUri/UriScheme.cs @@ -1,7 +1,7 @@ namespace WebExpress.WebCore.WebUri { /// - /// The typ of the uri. + /// The type of the URI. /// public enum UriScheme { @@ -40,4 +40,30 @@ public enum UriScheme /// Mailto } + + /// + /// Extension methods for the enum. + /// + public static class UriSchemeExtension + { + /// + /// Converts the to its string representation. + /// + /// The URI scheme to convert. + /// The string representation of the URI scheme. + public static string ToString(this UriScheme scheme) + { + return scheme switch + { + UriScheme.File => "file", + UriScheme.FTP => "ftp", + UriScheme.Http => "http", + UriScheme.Https => "https", + UriScheme.Ldap => "ldap", + UriScheme.Ldaps => "ldaps", + UriScheme.Mailto => "mailto", + _ => "http" + }; + } + } } \ No newline at end of file From e5a0db3b2233360894bd3e21bb27cd7aff19a125 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 29 Mar 2025 12:11:44 +0100 Subject: [PATCH 146/162] refactor: improve clock test --- .../Schedule/UnitTestClock.cs | 358 ++++++++---------- 1 file changed, 148 insertions(+), 210 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs index 7b86ad2..004ba48 100644 --- a/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs +++ b/src/WebExpress.WebCore.Test/Schedule/UnitTestClock.cs @@ -1,4 +1,4 @@ -using WebExpress.WebCore.Test.Fixture; +using System.Globalization; using WebExpress.WebCore.WebJob; namespace WebExpress.WebCore.Test.Schedule @@ -9,228 +9,166 @@ namespace WebExpress.WebCore.Test.Schedule [Collection("NonParallelTests")] public class UnitTestClock { - [Fact] - public void Synchronize_1() - { + /// + /// Test the synchronization of the clock. + /// + [Theory] + [InlineData(0, 0, 0, 0, 0)] + [InlineData(0, 0, 0, 30, 0)] + [InlineData(0, 0, -5, 0, 5)] + [InlineData(0, 0, 5, 0, 0)] + [InlineData(-1, 0, 0, 0, 24 * 60)] + [InlineData(-1, -10, 0, 0, (24 * 60) + (10 * 60))] + public void Synchronize(int? days, int? hours, int? minutes, int? seconds, int expected) + { + // preconditions var dateTime = DateTime.Now; - var clock = new Clock(DateTime.Now.AddMinutes(-5)); + if (days.HasValue) + { + dateTime = dateTime.AddDays(days.Value); + } - var elapsed = clock.Synchronize(); + if (hours.HasValue) + { + dateTime = dateTime.AddHours(hours.Value); + } - Assert.True - ( - elapsed.Count() == 5 - ); - } + if (minutes.HasValue) + { + dateTime = dateTime.AddMinutes(minutes.Value); + } - [Fact] - public void Synchronize_2() - { - var dateTime = DateTime.Now; + if (seconds.HasValue) + { + dateTime = dateTime.AddSeconds(seconds.Value); + } - var clock = new Clock(DateTime.Now.AddDays(-1)); + var clock = new Clock(dateTime); + // test execution var elapsed = clock.Synchronize(); - Assert.True - ( - elapsed.Count() == 60 * 24 - ); - } - - [Fact] - public void Compare_Equals_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 == clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_Equals_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(5)); - - var res = clock1 == clock2; - - Assert.True - ( - !res - ); - } - - [Fact] - public void Compare_Inequality_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 != clock2; - - Assert.True - ( - !res - ); - } - - [Fact] - public void Compare_Inequality_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(5)); - - var res = clock1 != clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_Less_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 < clock2; - - Assert.True - ( - !res - ); + Assert.Equal(expected, elapsed.Count()); } - [Fact] - public void Compare_Less_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(5)); - - var res = clock1 < clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_Greater_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 > clock2; - - Assert.True - ( - !res - ); - } - - [Fact] - public void Compare_Greater_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(-5).AddDays(-5)); - - var res = clock1 > clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_LessOrEqual_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 <= clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_LessOrEqual_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(5)); - - var res = clock1 <= clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_GreaterOrEqual_1() - { - var clock1 = new Clock(); - var clock2 = new Clock(clock1); - - var res = clock1 >= clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Compare_GreaterOrEqual_2() - { - var clock1 = new Clock(); - var clock2 = new Clock(DateTime.Now.AddMinutes(-5).AddDays(-5)); - - var res = clock1 >= clock2; - - Assert.True - ( - res - ); - } - - [Fact] - public void Carry_1() - { - var clock1 = new Clock(new DateTime(2020, 12, 31, 23, 59, 0)); - var clock2 = new Clock(new DateTime(2021, 1, 1, 0, 0, 0)); - clock1.Tick(); - - Assert.True - ( - clock1 == clock2 - ); - } - - [Fact] - public void Carry_2() - { - var clock1 = new Clock(new DateTime(2021, 2, 28, 23, 59, 0)); - var clock2 = new Clock(new DateTime(2021, 3, 1, 0, 0, 0)); + /// + /// Test the == operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", true)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", false)] + public void CompareEquals(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 == clock2); + } + + /// + /// Test the != operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", false)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", true)] + public void CompareInequality(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 != clock2); + } + + /// + /// Test the less operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", false)] + [InlineData("2021-01-01 00:00:00", "2020-12-31 23:59:00", false)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", true)] + public void CompareLess(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 < clock2); + } + + /// + /// Test the greater operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", false)] + [InlineData("2021-01-01 00:00:00", "2020-12-31 23:59:00", true)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", false)] + public void CompareGreater(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 > clock2); + } + + /// + /// Test the less or equal operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", true)] + [InlineData("2021-01-01 00:00:00", "2020-12-31 23:59:00", false)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", true)] + public void CompareLessOrEqual(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 <= clock2); + } + + /// + /// Test the greater or equals operator of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2020-12-31 23:59:00", true)] + [InlineData("2021-01-01 00:00:00", "2020-12-31 23:59:00", true)] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00", false)] + public void CompareGreaterOrEqual(string dateTime1, string dateTime2, bool expected) + { + // preconditions + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(dateTime2, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + + // test execution + Assert.Equal(expected, clock1 >= clock2); + } + + /// + /// Test the carry of the clock. + /// + [Theory] + [InlineData("2020-12-31 23:59:00", "2021-01-01 00:00:00")] + [InlineData("2021-02-27 23:58:00", "2021-02-27 23:59:00")] + [InlineData("2021-02-28 23:59:00", "2021-03-01 00:00:00")] + [InlineData("2024-02-28 23:59:00", "2024-02-29 00:00:00")] + [InlineData("2024-02-29 23:59:00", "2024-03-01 00:00:00")] + public void Tick(string dateTime1, string expected) + { + var clock1 = new Clock(DateTime.ParseExact(dateTime1, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); + var clock2 = new Clock(DateTime.ParseExact(expected, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); clock1.Tick(); - Assert.True - ( - clock1 == clock2 - ); + // test execution + Assert.Equal(clock2, clock1); } } } From feb8eaaed6dd1a2a3b54b6c3ca1e214467002e3e Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 29 Mar 2025 12:56:59 +0100 Subject: [PATCH 147/162] fix: handle endpoints outside the www directory --- .../WebEndpoint/RouteEndpoint.cs | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs index 27cc094..2e87a95 100644 --- a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs +++ b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs @@ -170,7 +170,7 @@ public virtual IRoute Concat(params IUriPathSegment[] segments) /// /// The routes to be combine. /// A combined route. - public static IRoute Combine(params IRoute[] routes) + public static RouteEndpoint Combine(params IRoute[] routes) { var r = routes.Skip(1).Where(x => !x.IsRoot).SelectMany(x => x.PathSegments.Skip(1)); var copy = new RouteEndpoint(routes.FirstOrDefault()); @@ -186,12 +186,15 @@ public static IRoute Combine(params IRoute[] routes) /// The base route to be used as the starting point. /// The routes to be combine. /// A combined route. - public static IRoute Combine(IRoute baseRoute, params string[] routes) + public static RouteEndpoint Combine(IRoute baseRoute, params string[] routes) { var copy = new RouteEndpoint(baseRoute); copy.PathSegments = copy.PathSegments - .Concat(routes.Where(x => !string.IsNullOrWhiteSpace(x)) - .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries)) + .Concat(routes.Where + ( + x => !string.IsNullOrWhiteSpace(x)) + .SelectMany(x => x.Split('/', StringSplitOptions.RemoveEmptyEntries) + ) .Select(x => new UriPathSegmentConstant(x) as IUriPathSegment)); return copy; @@ -203,7 +206,7 @@ public static IRoute Combine(IRoute baseRoute, params string[] routes) /// The base route serving as the starting point. /// The enumerable collection of path segments to be added. /// A new compound route combining all specified components. - public static IRoute Combine(IRoute baseRoute, IEnumerable segments) + public static RouteEndpoint Combine(IRoute baseRoute, IEnumerable segments) { var copy = new RouteEndpoint(baseRoute); copy.PathSegments = copy.PathSegments @@ -213,12 +216,31 @@ public static IRoute Combine(IRoute baseRoute, IEnumerable segm } /// - /// Converts a resource uri to a normal uri. + /// Combines the specified base route, intermediate route, and enumerable segments into a compound route. + /// + /// The base route serving as the starting point. + /// The path segment(s) to be added. + /// A new compound route combining all specified components. + public static RouteEndpoint Combine(IRoute baseRoute, string segments) + { + var copy = new RouteEndpoint(baseRoute); + copy.PathSegments = copy.PathSegments + .Concat + ( + segments?.Split('/', StringSplitOptions.RemoveEmptyEntries) + ?.Select(x => new UriPathSegmentConstant(x)) ?? [] + ); + + return copy; + } + + /// + /// Converts a route to a string. /// - /// The uri to convert. - public static implicit operator string(RouteEndpoint uri) + /// The uri to convert. + public static implicit operator string(RouteEndpoint route) { - return uri?.ToString(); + return route?.ToString(); } /// From 2dea78cfc5f65a4efbe33fc78220b4d8b7ae6c0e Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 29 Mar 2025 12:58:11 +0100 Subject: [PATCH 148/162] add: additional combine method for route composition --- .../Route/UnitTestRoute.cs | 16 ++++++++++++++++ .../WebEndpoint/EndpointManager.cs | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs index 2980959..caee74c 100644 --- a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs +++ b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs @@ -59,5 +59,21 @@ public void CombineRoute(string baseRoute, string pathB, string expected) Assert.Equal(expected, combine.ToString()); } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c")] + [InlineData("/a/b/c", "", "/a/b/c")] + [InlineData("/a/b/c", "d", "/a/b/c/d")] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f")] + public void CombineSegment(string baseRoute, string segment, string expected) + { + // test execution + var combine = RouteEndpoint.Combine(new RouteEndpoint(baseRoute), segment); + + Assert.Equal(expected, combine.ToString()); + } } } diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index f5d3393..16e44c8 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -169,7 +169,7 @@ public static IRoute CreateEndpointRoute var className = _classSuffixes?.FirstOrDefault(s => classType.Name.ToLowerInvariant().EndsWith(s, StringComparison.OrdinalIgnoreCase)) is string suffix ? classType.Name.ToLowerInvariant()[..^suffix.Length] : classType.Name.ToLowerInvariant(); - var segments = (fullClassName.Length - className.Length - 1 > assemblyName.Length) + var segments = (fullClassName.Length - classType.Name.Length - 1 > assemblyName.Length) ? fullClassName[(assemblyName.Length + 1)..^(classType.Name.Length + 1)].ToLowerInvariant().Split('.', StringSplitOptions.RemoveEmptyEntries) : []; From c4802c8ab8aae2e1014e5a448c68168b5b52d0f1 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Sat, 29 Mar 2025 21:24:41 +0100 Subject: [PATCH 149/162] add: ToUri method --- src/WebExpress.WebCore/WebEndpoint/IRoute.cs | 8 ++++++++ src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/WebExpress.WebCore/WebEndpoint/IRoute.cs b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs index 7f0f560..9245f2c 100644 --- a/src/WebExpress.WebCore/WebEndpoint/IRoute.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebEndpoint @@ -44,5 +45,12 @@ public interface IRoute /// An array of path segments to be concatenated to the existing route. /// A new IRoute instance representing the route after concatenation. IRoute Concat(params IUriPathSegment[] segments); + + /// + /// Converts the route to a URI. + /// + /// The parameters to be included in the URI. + /// An instance of IUri representing the route as a URI. + IUri ToUri(params Parameter[] parameters); } } diff --git a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs index 2e87a95..e6eaf1c 100644 --- a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs +++ b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using WebExpress.WebCore.WebMessage; using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebEndpoint @@ -165,6 +166,16 @@ public virtual IRoute Concat(params IUriPathSegment[] segments) return copy; } + /// + /// Converts the route to a URI. + /// + /// The parameters to be included in the URI. + /// An instance of IUri representing the route as a URI. + public IUri ToUri(params Parameter[] parameters) + { + return new UriEndpoint(this).SetParameters(parameters); + } + /// /// Combines the specified routes into a compound route. /// From 05359b24e9d1b6994c92377edab4702e485bd0b2 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Tue, 1 Apr 2025 22:27:23 +0200 Subject: [PATCH 150/162] feat: bug fixes and general improvements --- .../Manager/UnitTestRestApiManager.cs | 22 +++++---- .../Manager/UnitTestSitemapManager.cs | 32 ++++++------- .../Route/UnitTestRoute.cs | 47 +++++++++++++++++-- .../Uri/UnitTestUri.cs | 44 ++++++++++++++--- .../WWW/Api/{ => 1}/TestRestApiA.cs | 3 +- .../WWW/Api/{ => 2}/TestRestApiB.cs | 3 +- .../WWW/Api/{ => 3}/TestRestApiC.cs | 3 +- .../WWW/Blog/Post/PostId/Index.cs | 1 + .../WWW/Blog/Post/PostId/SegmentInfo.cs | 14 +++--- src/WebExpress.WebCore/HttpServer.cs | 7 --- .../WebAsset/AssetContext.cs | 12 +---- .../WebAsset/AssetManager.cs | 26 ++++++++-- .../WebAttribute/VersionAttribute.cs | 20 -------- .../WebEndpoint/EndpointManager.cs | 30 ++++++------ src/WebExpress.WebCore/WebEndpoint/IRoute.cs | 7 +++ .../WebEndpoint/RouteEndpoint.cs | 28 +++++++++-- src/WebExpress.WebCore/WebPage/PageManager.cs | 8 +++- .../WebResource/ResourceAsset.cs | 2 +- .../WebResource/ResourceManager.cs | 8 +++- .../WebRestAPI/RestApiManager.cs | 38 ++++++++------- .../WebSettingPage/SettingPageManager.cs | 8 +++- .../WebSitemap/SitemapManager.cs | 40 ++++++++++++---- src/WebExpress.WebCore/WebUri/IUri.cs | 31 ++++-------- src/WebExpress.WebCore/WebUri/UriEndpoint.cs | 46 ++++-------------- 24 files changed, 279 insertions(+), 201 deletions(-) rename src/WebExpress.WebCore.Test/WWW/Api/{ => 1}/TestRestApiA.cs (97%) rename src/WebExpress.WebCore.Test/WWW/Api/{ => 2}/TestRestApiB.cs (97%) rename src/WebExpress.WebCore.Test/WWW/Api/{ => 3}/TestRestApiC.cs (97%) delete mode 100644 src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs index 769f3bb..c0d25cb 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestRestApiManager.cs @@ -1,5 +1,7 @@ using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.Test.WWW.Api; +using WebExpress.WebCore.Test.WWW.Api._1; +using WebExpress.WebCore.Test.WWW.Api._2; +using WebExpress.WebCore.Test.WWW.Api._3; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebRestApi; @@ -45,15 +47,15 @@ public void Remove() /// Test the id property of the rest api. /// [Theory] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] - [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] - [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "webexpress.webcore.test.www.api.testrestapia")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "webexpress.webcore.test.www.api.testrestapib")] - [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "webexpress.webcore.test.www.api.testrestapic")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiA), "webexpress.webcore.test.www.api._1.testrestapia")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiB), "webexpress.webcore.test.www.api._2.testrestapib")] + [InlineData(typeof(TestApplicationA), typeof(TestRestApiC), "webexpress.webcore.test.www.api._3.testrestapic")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiA), "webexpress.webcore.test.www.api._1.testrestapia")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiB), "webexpress.webcore.test.www.api._2.testrestapib")] + [InlineData(typeof(TestApplicationB), typeof(TestRestApiC), "webexpress.webcore.test.www.api._3.testrestapic")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiA), "webexpress.webcore.test.www.api._1.testrestapia")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiB), "webexpress.webcore.test.www.api._2.testrestapib")] + [InlineData(typeof(TestApplicationC), typeof(TestRestApiC), "webexpress.webcore.test.www.api._3.testrestapic")] public void Id(Type applicationType, Type resourceType, string id) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs index 49a9d7f..bd10883 100644 --- a/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs +++ b/src/WebExpress.WebCore.Test/Manager/UnitTestSitemapManager.cs @@ -1,5 +1,7 @@ using WebExpress.WebCore.Test.Fixture; -using WebExpress.WebCore.Test.WWW.Api; +using WebExpress.WebCore.Test.WWW.Api._1; +using WebExpress.WebCore.Test.WWW.Api._2; +using WebExpress.WebCore.Test.WWW.Api._3; using WebExpress.WebCore.Test.WWW.Resources; using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebSitemap; @@ -25,7 +27,7 @@ public void Refresh() // test execution componentManager.SitemapManager.Refresh(); - Assert.Equal(82, componentManager.SitemapManager.SiteMap.Count()); + Assert.Equal(79, componentManager.SitemapManager.SiteMap.Count()); } /// @@ -53,9 +55,9 @@ public void Refresh() [InlineData("http://localhost:8080/server", "webexpress.webcore.test.www.index")] [InlineData("http://localhost:8080/server/about", "webexpress.webcore.test.www.about")] [InlineData("http://localhost:8080/server/contact", "webexpress.webcore.test.www.contact")] - [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api.testrestapia")] - [InlineData("http://localhost:8080/server/appa/api/2/testrestapib", "webexpress.webcore.test.www.api.testrestapib")] - [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api.testrestapic")] + [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api._1.testrestapia")] + [InlineData("http://localhost:8080/server/appa/api/2/testrestapib", "webexpress.webcore.test.www.api._2.testrestapib")] + [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api._3.testrestapic")] [InlineData("http://localhost:8080/server/appa/assets/css/mycss.css", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js/myjavascript.mini.js", "webexpress.webcore.asset")] @@ -63,7 +65,6 @@ public void Refresh() [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/uri/does/not/exist", null)] - public void SearchResource(string uri, string id) { // preconditions @@ -80,7 +81,7 @@ public void SearchResource(string uri, string id) HttpContext = context }); - componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock(), searchResult.EndpointContext); + componentHub.EndpointManager.HandleRequest(UnitTestFixture.CrerateRequestMock(), searchResult?.EndpointContext); Assert.Equal(id, searchResult?.EndpointContext?.EndpointId.ToString()); } @@ -108,8 +109,8 @@ public void SearchResource(string uri, string id) [InlineData(typeof(TestApplicationA), typeof(WWW.Blog.Post.PostId.Edit), 1, "/server/appa/blog/post/1/edit")] [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Index), null, "/server/appa/products")] [InlineData(typeof(TestApplicationA), typeof(WWW.Products.List), null, "/server/appa/products/list")] - [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), null, "/server/appa/products/details/${testparametera}")] - [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), 2, "/server/appa/products/details/2")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), null, "/server/appa/products/${testparametera}")] + [InlineData(typeof(TestApplicationA), typeof(WWW.Products.Details.Index), 2, "/server/appa/products/2")] public void GetUri(Type applicationType, Type resourceType, int? param, string expected) { // preconditions @@ -138,9 +139,9 @@ public void GetUri(Type applicationType, Type resourceType, int? param, string e [InlineData("http://localhost:8080/server/appa/assets/css.mycss.css", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.js", "webexpress.webcore.asset")] [InlineData("http://localhost:8080/server/appa/assets/js.myjavascript.mini.js", "webexpress.webcore.asset")] - [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api.testrestapia")] - [InlineData("http://localhost:8080/server/appa/api/2/TestRestApiB", "webexpress.webcore.test.www.api.testrestapib")] - [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api.testrestapic")] + [InlineData("http://localhost:8080/server/appa/api/1/testrestapia", "webexpress.webcore.test.www.api._1.testrestapia")] + [InlineData("http://localhost:8080/server/appa/api/2/TestRestApiB", "webexpress.webcore.test.www.api._2.testrestapib")] + [InlineData("http://localhost:8080/server/appa/api/3/testrestapic", "webexpress.webcore.test.www.api._3.testrestapic")] [InlineData("http://localhost:8080/server/appa", "webexpress.webcore.test.www.index")] [InlineData("http://localhost:8080/server/appa/", "webexpress.webcore.test.www.index")] [InlineData("http://localhost:8080/server/appa/about", "webexpress.webcore.test.www.about")] @@ -154,12 +155,11 @@ public void GetUri(Type applicationType, Type resourceType, int? param, string e [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123/edit", "webexpress.webcore.test.www.blog.post.postid.edit")] [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123", "webexpress.webcore.test.www.blog.post.postid.index")] [InlineData("http://localhost:8080/server/appa/blog/post/10E96737-5C72-4C25-9E74-F96D8863D123/", "webexpress.webcore.test.www.blog.post.postid.index")] + [InlineData("http://localhost:8080/server/appa/Products", "webexpress.webcore.test.www.products.index")] [InlineData("http://localhost:8080/server/appa/Products/", "webexpress.webcore.test.www.products.index")] - [InlineData("http://localhost:8080/server/appa/Products/Index", "webexpress.webcore.test.www.products.index")] [InlineData("http://localhost:8080/server/appa/Products/list", "webexpress.webcore.test.www.products.list")] - [InlineData("http://localhost:8080/server/appa/products/details", "webexpress.webcore.test.www.products.details.index")] - [InlineData("http://localhost:8080/server/appa/products/details/10E96737-5C72-4C25-9E74-F96D8863D123", "webexpress.webcore.test.www.products.details.index")] - [InlineData("http://localhost:8080/server/appa/products/details/10E96737-5C72-4C25-9E74-F96D8863D123/", "webexpress.webcore.test.www.products.details.index")] + [InlineData("http://localhost:8080/server/appa/products/10E96737-5C72-4C25-9E74-F96D8863D123", "webexpress.webcore.test.www.products.details.index")] + [InlineData("http://localhost:8080/server/appa/products/10E96737-5C72-4C25-9E74-F96D8863D123/", "webexpress.webcore.test.www.products.details.index")] public void GetEndpoint(string uri, string expected) { // preconditions diff --git a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs index caee74c..862f497 100644 --- a/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs +++ b/src/WebExpress.WebCore.Test/Route/UnitTestRoute.cs @@ -1,4 +1,5 @@ using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Route { @@ -16,13 +17,33 @@ public class UnitTestRoute [InlineData("/a/b/c", "", "/a/b/c", 4)] [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] - public void Concat(string baseRoute, string segment, string expected, int count) + public void ConcatString(string baseRoute, string segment, string expected, int count) { // preconditions - var uri = new RouteEndpoint(baseRoute); + var route = new RouteEndpoint(baseRoute); // test execution - var concat = uri.Concat(segment); + var concat = route.Concat(segment); + + Assert.Equal(expected, concat.ToString()); + Assert.Equal(count, concat.PathSegments.Count()); + } + + /// + /// Test the concat method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c", 4)] + [InlineData("/a/b/c", " ", "/a/b/c", 4)] + [InlineData("/a/b/c", "d", "/a/b/c/d", 5)] + [InlineData("/a/b/c", "/d/e/f", "/a/b/c/d/e/f", 7)] + public void ConcatSegment(string baseRoute, string segment, string expected, int count) + { + // preconditions + var route = new RouteEndpoint(baseRoute); + + // test execution + var concat = route.Concat(segment != null ? [.. segment?.Split('/').Select(x => new UriPathSegmentConstant(x))] : null); Assert.Equal(expected, concat.ToString()); Assert.Equal(count, concat.PathSegments.Count()); @@ -75,5 +96,25 @@ public void CombineSegment(string baseRoute, string segment, string expected) Assert.Equal(expected, combine.ToString()); } + + /// + /// Test the combine method. + /// + [Theory] + [InlineData("/a/b/c", null, "/a/b/c")] + [InlineData("/a/b/c", "", "/a/b/c")] + [InlineData("/a/b/c", "b", "/a/c")] + [InlineData("/a/b/c", "/b", "/a/c")] + [InlineData("/a/b/c", "/b/c", "/a")] + [InlineData("/a/b/c", "/a/c", "/a/b/c")] + public void RemoveSegment(string route, string segment, string expected) + { + // test execution + var routeEndpoint = new RouteEndpoint(route); + + var removed = routeEndpoint.RemoveSegment(segment); + + Assert.Equal(expected, removed.ToString()); + } } } diff --git a/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs index b6886e4..6417065 100644 --- a/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs +++ b/src/WebExpress.WebCore.Test/Uri/UnitTestUri.cs @@ -1,4 +1,5 @@ -using WebExpress.WebCore.WebUri; +using WebExpress.WebCore.WebEndpoint; +using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.Test.Uri { @@ -142,20 +143,49 @@ public void Take(string path, int takeCount, string expected) } /// - /// Test the extended path property. + /// Test the merge method. /// [Theory] - [InlineData("http://user@example.com/x/y/z", "http://user@example.com/", "/x/y/z")] - [InlineData("http://user@example.com/a/b/c/x/y/z", "http://user@example.com/a/b/c", "/x/y/z")] - public void ExtendedPath(string uri, string endpointUri, string expected) + [InlineData("http://www.example.com", "/a/b/c", "http://www.example.com/a/b/c")] + [InlineData("http://www.example.com/", "/a/b/c", "http://www.example.com/a/b/c")] + [InlineData("http://www.example.com/a/b/c", "/a/b/c", "http://www.example.com/a/b/c")] + [InlineData("http://www.example.com/a/$guid/c", "/a/$guid/c", "http://www.example.com/a/$guid/c")] + public void Merge(string uri, string route, string expected) + { + // preconditions + var random = Guid.NewGuid().ToString(); + var uriEndpoint = new UriEndpoint(uri.Replace("$guid", random)); + var routeEndpoint = new RouteEndpoint + ( + [.. route.Split('/').Select + ( + x => (IUriPathSegment)(x == "$guid" + ? new UriPathSegmentVariableGuid("guid") { Value = random } + : new UriPathSegmentConstant(x)) + )] + ); + + // test execution + var resourceUri = new UriEndpoint(uriEndpoint, routeEndpoint.PathSegments); + + Assert.Equal(expected.Replace("$guid", random), resourceUri?.ToString()); + } + + /// + /// Test the base path property. + /// + [Theory] + [InlineData("http://user@example.com/x/y/z", "http://user@example.com/x", "http://user@example.com/x")] + [InlineData("http://user@example.com/a/b/c/x/y/z", "http://user@example.com/a/b/c", "http://user@example.com/a/b/c")] + public void BasePath(string uri, string baseUri, string expected) { var resourceUri = new UriEndpoint(uri) { - EndpointRoot = new UriEndpoint(endpointUri) + BasePath = new UriEndpoint(baseUri) }; Assert.Equal(uri, resourceUri.ToString()); - Assert.Equal(expected, resourceUri.ExtendedPath.ToString()); + Assert.Equal(expected, resourceUri.BasePath.ToString()); } } } diff --git a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs b/src/WebExpress.WebCore.Test/WWW/Api/1/TestRestApiA.cs similarity index 97% rename from src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs rename to src/WebExpress.WebCore.Test/WWW/Api/1/TestRestApiA.cs index 7d2c436..3c4f47c 100644 --- a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiA.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/1/TestRestApiA.cs @@ -1,14 +1,13 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test.WWW.Api +namespace WebExpress.WebCore.Test.WWW.Api._1 { /// /// A dummy class for testing purposes. /// [Method(CrudMethod.POST)] [Method(CrudMethod.GET)] - [Version(1)] public sealed class TestRestApiA : IRestApi { /// diff --git a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs b/src/WebExpress.WebCore.Test/WWW/Api/2/TestRestApiB.cs similarity index 97% rename from src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs rename to src/WebExpress.WebCore.Test/WWW/Api/2/TestRestApiB.cs index ce98eea..b4d11a8 100644 --- a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiB.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/2/TestRestApiB.cs @@ -1,13 +1,12 @@ using WebExpress.WebCore.WebAttribute; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test.WWW.Api +namespace WebExpress.WebCore.Test.WWW.Api._2 { /// /// A dummy class for testing purposes. /// [Method(CrudMethod.GET)] - [Version(2)] public sealed class TestRestApiB : IRestApi { /// diff --git a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs b/src/WebExpress.WebCore.Test/WWW/Api/3/TestRestApiC.cs similarity index 97% rename from src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs rename to src/WebExpress.WebCore.Test/WWW/Api/3/TestRestApiC.cs index b9b6d71..920d4de 100644 --- a/src/WebExpress.WebCore.Test/WWW/Api/TestRestApiC.cs +++ b/src/WebExpress.WebCore.Test/WWW/Api/3/TestRestApiC.cs @@ -2,13 +2,12 @@ using WebExpress.WebCore.WebComponent; using WebExpress.WebCore.WebRestApi; -namespace WebExpress.WebCore.Test.WWW.Api +namespace WebExpress.WebCore.Test.WWW.Api._3 { /// /// A dummy class for testing purposes. /// [Method(CrudMethod.GET)] - [Version(3)] public sealed class TestRestApiC : RestApi { /// diff --git a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs index a27f087..130a23d 100644 --- a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/Index.cs @@ -7,6 +7,7 @@ namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId /// A dummy class for testing purposes. /// [Title("webindex:index.label")] + [SegmentGuid("segment")] public sealed class Index : IPage { /// diff --git a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs index a2adf9f..85915e9 100644 --- a/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs +++ b/src/WebExpress.WebCore.Test/WWW/Blog/Post/PostId/SegmentInfo.cs @@ -1,10 +1,8 @@ -using WebExpress.WebCore.WebAttribute; - -namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId +namespace WebExpress.WebCore.Test.WWW.Blog.Post.PostId { - [Name("webindex:segment.label")] - [SegmentGuid("segment")] - public sealed class SegmentInfo - { - } + //[Name("webindex:segment.label")] + //[SegmentGuid("segment")] + //public sealed class SegmentInfo + //{ + //} } diff --git a/src/WebExpress.WebCore/HttpServer.cs b/src/WebExpress.WebCore/HttpServer.cs index 706f47d..dfb0a95 100644 --- a/src/WebExpress.WebCore/HttpServer.cs +++ b/src/WebExpress.WebCore/HttpServer.cs @@ -264,13 +264,6 @@ private Response HandleClient(HttpContext context) if (searchResult != null) { var resourceUri = new UriEndpoint(request.Uri, searchResult.Uri.PathSegments); - resourceUri = new UriEndpoint((IUri)resourceUri) - { - ServerRoot = new UriEndpoint(request.Uri, HttpServerContext.ContextPath.PathSegments), - ApplicationRoot = new UriEndpoint(request.Uri, searchResult.EndpointContext?.ApplicationContext?.ContextPath.PathSegments), - EndpointRoot = new UriEndpoint(request.Uri, searchResult.Uri.PathSegments) - }; - request.Uri = resourceUri; try diff --git a/src/WebExpress.WebCore/WebAsset/AssetContext.cs b/src/WebExpress.WebCore/WebAsset/AssetContext.cs index 616dfbe..f8b15a0 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetContext.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetContext.cs @@ -5,7 +5,6 @@ using WebExpress.WebCore.WebCondition; using WebExpress.WebCore.WebEndpoint; using WebExpress.WebCore.WebPlugin; -using WebExpress.WebCore.WebUri; namespace WebExpress.WebCore.WebAsset { @@ -14,9 +13,6 @@ namespace WebExpress.WebCore.WebAsset /// public class AssetContext : IAssetContext { - private readonly IRoute _contextPath; - private readonly IUriPathSegment _pathSegment; - /// /// Returns the associated plugin context. /// @@ -50,7 +46,7 @@ public class AssetContext : IAssetContext /// /// Returns the internal routing path for the endpoint. /// - public IRoute Route => RouteEndpoint.Combine(ApplicationContext.ContextPath, _contextPath.Concat(_pathSegment)); + public IRoute Route { get; internal set; } /// /// Returns the attributes associated with the page. @@ -60,12 +56,8 @@ public class AssetContext : IAssetContext /// /// Initializes a new instance of the class with the specified endpoint manager, parent type, context path, and path segment. /// - /// The context path of the resource. - /// The path segment of the resource. - public AssetContext(IRoute contextPath, IUriPathSegment pathSegment) + public AssetContext() { - _contextPath = contextPath; - _pathSegment = pathSegment; } /// diff --git a/src/WebExpress.WebCore/WebAsset/AssetManager.cs b/src/WebExpress.WebCore/WebAsset/AssetManager.cs index ada9019..44b7876 100644 --- a/src/WebExpress.WebCore/WebAsset/AssetManager.cs +++ b/src/WebExpress.WebCore/WebAsset/AssetManager.cs @@ -142,11 +142,21 @@ private void Register(IPluginContext pluginContext, IEnumerable(typeof(Asset), assetContext, _httpServerContext, _componentHub, resource) + Instance = ComponentActivator.CreateInstance + ( + typeof(Asset), + assetContext, + _httpServerContext, + _componentHub, + resource + ) }; if (_itemDictionary.AddAssetItem(pluginContext, applicationContext, assetItem)) @@ -267,7 +284,7 @@ private void OnAddApplication(object sender, IApplicationContext e) var assembly = typeof(AssetManager).Assembly; var assemblyName = assembly.GetName().Name.ToLower(); - var context = new AssetContext(new RouteEndpoint(), new UriPathSegmentConstant("assets")) + var context = new AssetContext() { ApplicationContext = e, PluginContext = new PluginContext() @@ -276,7 +293,8 @@ private void OnAddApplication(object sender, IApplicationContext e) Assembly = assembly }, EndpointId = new ComponentId(assemblyName + ".asset"), - IncludeSubPaths = true + IncludeSubPaths = true, + Route = RouteEndpoint.Combine(e.ContextPath, "assets") }; var asset = ComponentActivator.CreateInstance(typeof(Asset), context, _httpServerContext, _componentHub); diff --git a/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs b/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs deleted file mode 100644 index 3cfd8dc..0000000 --- a/src/WebExpress.WebCore/WebAttribute/VersionAttribute.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace WebExpress.WebCore.WebAttribute -{ - /// - /// Specifying a version for a rest api. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class VersionAttribute : Attribute, IRestApiAttribute - { - /// - /// Initializes a new instance of the class. - /// - /// The version number of the rest api. - public VersionAttribute(uint version) - { - - } - } -} diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index 16e44c8..d7ad7d5 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -150,7 +150,7 @@ public void Dispose() /// Returns the route of an endpoint based on the class type, application context and segment attributes. /// /// The type of the class. - /// The application context. + /// The application context route. /// The segment attribute. /// The intermediate segments. /// The namespace prefixes. @@ -158,7 +158,7 @@ public void Dispose() public static IRoute CreateEndpointRoute ( Type classType, - IApplicationContext applicationContext, + IRoute contextRoute, ISegmentAttribute segment, IEnumerable intermediateSegments = null, string[] namespacePrefixes = null @@ -173,18 +173,20 @@ public static IRoute CreateEndpointRoute ? fullClassName[(assemblyName.Length + 1)..^(classType.Name.Length + 1)].ToLowerInvariant().Split('.', StringSplitOptions.RemoveEmptyEntries) : []; - var segmentAttributesMapping = segments.Select((segment, index) => new + var segmentMapping = segments.Select((segment, index) => new { FullNamespace = $"{assemblyName}.{string.Join(".", segments.Take(index + 1))}", Segment = segment - }).Select(s => + }); + + var segmentAttributesMapping = segmentMapping.Select(s => { var segmentResult = default(IUriPathSegment); var name = default(string); var description = default(string); var icon = default(IIcon); - var typeName = $"{s.FullNamespace}.SegmentInfo"; + var typeName = $"{s.FullNamespace}.Index"; var segmentInfoType = classType.Assembly.GetType(typeName, throwOnError: false, ignoreCase: true); if (segmentInfoType != null) @@ -198,7 +200,7 @@ public static IRoute CreateEndpointRoute ? segmentInfoType.GetCustomAttribute(segAttrType, false) as ISegmentAttribute : null; var nameAttr = segmentInfoType.CustomAttributes - .FirstOrDefault(x => x.AttributeType == typeof(NameAttribute)); + .FirstOrDefault(x => x.AttributeType == typeof(TitleAttribute)); var descAttr = segmentInfoType.CustomAttributes .FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute)); var iconAttr = segmentInfoType.CustomAttributes @@ -236,14 +238,14 @@ public static IRoute CreateEndpointRoute ? segmentAttributesMapping.Skip(1) : segmentAttributesMapping; - var uri = RouteEndpoint.Combine - ( - applicationContext.ContextPath, - (intermediateSegments ?? []) - .Concat(segmentAttributesMapping - .Select(x => x.Segment)) - ) - .Concat(segment?.ToPathSegment() ?? new UriPathSegmentConstant(!className.StartsWith(_indexPrefix) ? className : null)); + var endpointRoute = (intermediateSegments ?? []) + .Concat(segmentAttributesMapping.Select(x => x.Segment)); + + var classSegment = !className.StartsWith(_indexPrefix) + ? segment?.ToPathSegment() ?? new UriPathSegmentConstant(className) + : null; + var uri = RouteEndpoint.Combine(contextRoute, endpointRoute) + .Concat(classSegment); return uri; } diff --git a/src/WebExpress.WebCore/WebEndpoint/IRoute.cs b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs index 9245f2c..39f8929 100644 --- a/src/WebExpress.WebCore/WebEndpoint/IRoute.cs +++ b/src/WebExpress.WebCore/WebEndpoint/IRoute.cs @@ -46,6 +46,13 @@ public interface IRoute /// A new IRoute instance representing the route after concatenation. IRoute Concat(params IUriPathSegment[] segments); + /// + /// Removes a specified segment from the route and returns a new instance of IRoute with the updated path. + /// + /// The path segment to be removed from the existing route. + /// A new IRoute instance representing the route after the segment removal. + IRoute RemoveSegment(string segments); + /// /// Converts the route to a URI. /// diff --git a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs index e6eaf1c..9666f69 100644 --- a/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs +++ b/src/WebExpress.WebCore/WebEndpoint/RouteEndpoint.cs @@ -82,7 +82,7 @@ public RouteEndpoint(params IUriPathSegment[] segments) if (segments.Length > 0) { PathSegments = PathSegments - .Concat(segments.Select(x => x.Copy())); + .Concat(segments.Where(x => !x.IsEmpty).Select(x => x.Copy())); } } @@ -153,7 +153,7 @@ public IRoute Concat(string segment) /// A new IRoute instance representing the route after concatenation. public virtual IRoute Concat(params IUriPathSegment[] segments) { - if (segments.Length == 0) + if (segments == null || segments.Length == 0) { return this; } @@ -161,7 +161,29 @@ public virtual IRoute Concat(params IUriPathSegment[] segments) var copy = new RouteEndpoint((IRoute)this); copy.PathSegments = copy.PathSegments .Select(x => x.Copy()) - .Concat(segments.Where(x => !x.IsEmpty)); + .Concat(segments.Where(x => x != null).Where(x => !x.IsEmpty)); + + return copy; + } + + /// + /// Removes a specified segment from the route and returns a new instance of IRoute with the updated path. + /// + /// The path segment to be removed from the existing route. + /// A new IRoute instance representing the route after the segment removal. + public virtual IRoute RemoveSegment(string segments) + { + if (string.IsNullOrWhiteSpace(segments) || !ToString().Contains(segments)) + { + return this; + } + + var segmentParts = segments.Split('/', StringSplitOptions.RemoveEmptyEntries); + var copy = new RouteEndpoint((IRoute)this); + + copy.PathSegments = copy.PathSegments + .Where(x => !segmentParts.Contains(x.Value)) + .Select(x => x.Copy()); return copy; } diff --git a/src/WebExpress.WebCore/WebPage/PageManager.cs b/src/WebExpress.WebCore/WebPage/PageManager.cs index b2fec2f..60c7f1e 100644 --- a/src/WebExpress.WebCore/WebPage/PageManager.cs +++ b/src/WebExpress.WebCore/WebPage/PageManager.cs @@ -353,7 +353,13 @@ private void Register(IPluginContext pluginContext, IEnumerable x.StartsWith(AssetDirectory, System.StringComparison.OrdinalIgnoreCase)); - var url = request.Uri.ExtendedPath.ToString(); + var url = request.Uri.ToString(); var fileName = Path.GetFileName(url); var file = string.Join('.', AssetDirectory.Trim('.'), "assets", url.Replace("/", ".").Trim('.')); diff --git a/src/WebExpress.WebCore/WebResource/ResourceManager.cs b/src/WebExpress.WebCore/WebResource/ResourceManager.cs index 1ebcaa8..6ddd4ae 100644 --- a/src/WebExpress.WebCore/WebResource/ResourceManager.cs +++ b/src/WebExpress.WebCore/WebResource/ResourceManager.cs @@ -162,7 +162,13 @@ private void Register(IPluginContext pluginContext, IEnumerable /// The rest api manager manages rest api resources, which can be called with a URI (Uniform page Identifier). /// - public class RestApiManager : IRestApiManager + public partial class RestApiManager : IRestApiManager { private readonly IComponentHub _componentHub; private readonly IHttpServerContext _httpServerContext; private readonly RestApiDictionary _dictionary = []; private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true }; + [GeneratedRegex(@"\.(?:_|V|v)(\d+)\.")] + private static partial Regex ApiVersionRegex(); + /// /// An event that fires when an rest api resource is added. /// @@ -315,7 +319,9 @@ private void Register(IPluginContext pluginContext, IEnumerable(); var cache = false; var methods = new List(); - var version = -1; + var match = ApiVersionRegex().Match(id); + var versionSegment = match.Success ? match.Groups[0].Value.Replace(".", "") : ""; + var version = match.Success && uint.TryParse(match.Groups[1].Value, out var result) ? result : 1u; var attributes = restApiType.CustomAttributes .Where(x => !x.AttributeType.GetInterfaces().Contains(typeof(IEndpointAttribute)) && !x.AttributeType.GetInterfaces().Contains(typeof(IPageAttribute))); @@ -353,28 +359,24 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType.GetInterfaces().Contains(typeof(IRestApiAttribute)))) - { - if (customAttribute.AttributeType.Name == typeof(VersionAttribute).Name - && customAttribute.AttributeType.Namespace == typeof(VersionAttribute).Namespace) - { - version = Convert.ToInt32(customAttribute.ConstructorArguments.FirstOrDefault().Value); - } - } - // assign the rest api to existing applications foreach (var applicationContext in applicationContexts) { + var prefix = applicationContext.ContextPath.Concat + ( + applicationContext.PluginContext != pluginContext + ? pluginContext.PluginName.ToLower() + : "" + ); + var routePath = EndpointManager.CreateEndpointRoute ( restApiType, - applicationContext, + prefix, segment, - [new UriPathSegmentConstant("api"), new UriPathSegmentVariableInt($"{version}") { VariableName = "apiVersion" }], + [new UriPathSegmentConstant("api"), new UriPathSegmentVariableInt($"{version}") { VariableName = "_apiVersion" }], ["api", "restapi", "rest"] - ); - var versionPath = version < 1 ? "" : version.ToString(); + ).RemoveSegment(versionSegment); var restApiContext = new RestApiContext() { @@ -386,7 +388,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.AttributeType), - Version = version < 1 ? 1u : (uint)version, + Version = version, Methods = methods.Distinct() }; @@ -398,7 +400,7 @@ private void Register(IPluginContext pluginContext, IEnumerable x.Route != null) .Select(x => new { EndpointContext = x, @@ -373,7 +374,7 @@ private static void MergeSitemap(SitemapNode first, SitemapNode second) /// The path segments. /// The search context. /// The search result with the found resource - private static SearchResult SearchNode + private SearchResult SearchNode ( SitemapNode node, Queue inPathSegments, @@ -402,16 +403,40 @@ SearchContext searchContext { EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriEndpoint([.. outPathSegments]) + Uri = new UriEndpoint + ( + [.. + outPathSegments.Concat(inPathSegments + .Select(x => new UriPathSegmentConstant(x))) + ] + ) + { + BasePath = new UriEndpoint([.. outPathSegments]) + } }; } - else if (node.IsLeaf && nextPathSegment != null && node.EndpointContext != null && node.EndpointContext.IncludeSubPaths) + else if + ( + node.IsLeaf + && nextPathSegment != null + && node.EndpointContext != null + && node.EndpointContext.IncludeSubPaths + ) { return new SearchResult() { EndpointContext = node.EndpointContext, SearchContext = searchContext, - Uri = new UriEndpoint([.. outPathSegments]) + Uri = new UriEndpoint + ( + [.. + outPathSegments.Concat(inPathSegments + .Select(x => new UriPathSegmentConstant(x))) + ] + ) + { + BasePath = new UriEndpoint([.. outPathSegments]) + } }; } @@ -422,12 +447,7 @@ SearchContext searchContext } // 404 - return new SearchResult() - { - EndpointContext = node.EndpointContext, - SearchContext = searchContext, - Uri = new UriEndpoint([.. outPathSegments]) - }; + return null; } /// diff --git a/src/WebExpress.WebCore/WebUri/IUri.cs b/src/WebExpress.WebCore/WebUri/IUri.cs index edacef2..c8ecfa0 100644 --- a/src/WebExpress.WebCore/WebUri/IUri.cs +++ b/src/WebExpress.WebCore/WebUri/IUri.cs @@ -27,11 +27,15 @@ public interface IUri IEnumerable PathSegments { get; } /// - /// Returns the extended segment of the endpoint's path, which is included only when the endpoint class has the IncludeSubPaths attribute enabled. - /// For example, if the core endpoint is "http://example.com/server/app/endpoint" and the extended segment is "extended", - /// the complete URI becomes "http://example.com/server/app/endpoint/extended". In this case, the property returns the "extended" part. + /// Returns or sets the base path of the endpoint's URI. + /// The base path is included only when the endpoint class has the IncludeSubPaths attribute enabled. + /// For example, if the complete URI is "http://example.com/server/app/endpoint/extended", + /// the BasePath property will represent the "http://example.com/server/app/endpoint" portion of the URI. /// - IUri ExtendedPath { get; } + /// + /// The base path as an object, or null if the IncludeSubPaths attribute is not enabled. + /// + public IUri BasePath { get; set; } /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). @@ -53,25 +57,6 @@ public interface IUri /// bool Empty { get; } - /// - /// Retrieves the base URI of the endpoint. When the IncludeSubPaths attribute is enabled on the endpoint class, - /// the complete URI may include extra path segments. For example, the core endpoint could be - /// "http://example.com/server/app/endpoint", but with IncludeSubPaths enabled, the full URI might become - /// "http://example.com/server/app/endpoint/extended". In such cases, this property returns only the base URI: - /// "http://example.com/server/app/endpoint". - /// - IUri EndpointRoot { get; } - - /// - /// Returns the root of the application. - /// - IUri ApplicationRoot { get; } - - /// - /// Returns the root of the server. - /// - IUri ServerRoot { get; } - /// /// Determines if the Uri is the root. /// diff --git a/src/WebExpress.WebCore/WebUri/UriEndpoint.cs b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs index a51079b..6348439 100644 --- a/src/WebExpress.WebCore/WebUri/UriEndpoint.cs +++ b/src/WebExpress.WebCore/WebUri/UriEndpoint.cs @@ -44,17 +44,15 @@ public partial class UriEndpoint : IUri public IEnumerable PathSegments { get; private set; } = [new UriPathSegmentRoot()]; /// - /// Returns the extended segment of the endpoint's path, which is included only when the endpoint class has the IncludeSubPaths attribute enabled. - /// For example, if the core endpoint is "http://example.com/server/app/endpoint" and the extended segment is "extended", - /// the complete URI becomes "http://example.com/server/app/endpoint/extended". In this case, the property returns the "extended" part. + /// Returns or sets the base path of the endpoint's URI. + /// The base path is included only when the endpoint class has the IncludeSubPaths attribute enabled. + /// For example, if the complete URI is "http://example.com/server/app/endpoint/extended", + /// the BasePath property will represent the "http://example.com/server/app/endpoint" portion of the URI. /// - public IUri ExtendedPath - { - get - { - return new UriEndpoint(Skip(EndpointRoot.PathSegments.Count()).PathSegments?.ToArray()); - } - } + /// + /// The base path as an object, or null if the IncludeSubPaths attribute is not enabled. + /// + public IUri BasePath { get; set; } /// /// The query part (e.g. ?title=Uniform_Resource_Identifier). @@ -95,25 +93,6 @@ public virtual string Display /// public bool Empty => !PathSegments.Any(); - /// - /// Retrieves the base URI of the endpoint. When the IncludeSubPaths attribute is enabled on the endpoint class, - /// the complete URI may include extra path segments. For example, the core endpoint could be - /// "http://example.com/server/app/endpoint", but with IncludeSubPaths enabled, the full URI might become - /// "http://example.com/server/app/endpoint/extended". In such cases, this property returns only the base URI: - /// "http://example.com/server/app/endpoint". - /// - public virtual IUri EndpointRoot { get; set; } - - /// - /// Returns the root of the application. - /// - public virtual IUri ApplicationRoot { get; set; } - - /// - /// Returns the root of the server. - /// - public virtual IUri ServerRoot { get; set; } - /// /// Determines if the Uri is the root. /// @@ -229,9 +208,6 @@ public UriEndpoint(IUri uri) PathSegments = uri?.PathSegments.Select(x => x.Copy()) ?? []; Query = uri?.Query.Select(x => new UriQuery(x.Key, x.Value)) ?? []; Fragment = uri?.Fragment; - ServerRoot = uri?.ServerRoot; - ApplicationRoot = uri?.ApplicationRoot; - EndpointRoot = uri?.EndpointRoot; } /// @@ -256,9 +232,6 @@ public UriEndpoint(params IUriPathSegment[] segments) public UriEndpoint(IUri uri, IEnumerable segments) : this(uri.Scheme, uri.Authority, uri.Fragment, uri.Query, segments) { - ServerRoot = uri.ServerRoot; - ApplicationRoot = uri.ApplicationRoot; - EndpointRoot = uri.EndpointRoot; } /// @@ -270,8 +243,6 @@ public UriEndpoint(IUri uri, IEnumerable segments) public UriEndpoint(IUri uri, IEnumerable segments, IEnumerable extendedSegments) : this(uri.Scheme, uri.Authority, uri.Fragment, uri.Query, extendedSegments != null ? segments.Union(extendedSegments) : segments) { - ServerRoot = uri.ServerRoot; - ApplicationRoot = uri.ApplicationRoot; } /// @@ -286,7 +257,6 @@ public UriEndpoint(UriScheme scheme, UriAuthority authority, string fragment, IE { Scheme = scheme; Authority = authority; - PathSegments = PathSegments.Concat([new UriPathSegmentRoot()]); PathSegments = PathSegments.Concat(segments?.Where(x => x is not UriPathSegmentRoot).Select(x => x.Copy()) ?? []); Query = query.Select(x => new UriQuery(x.Key, x.Value)); Fragment = fragment; From 4ebf2690ef2c017669fbb93f5767a3e31447bd07 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 18 Apr 2025 10:22:14 +0200 Subject: [PATCH 151/162] feat: bug fixes and general improvements --- src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs | 2 +- src/WebExpress.WebCore/WebPackage/PackageManager.cs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs index d7ad7d5..a87b971 100644 --- a/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs +++ b/src/WebExpress.WebCore/WebEndpoint/EndpointManager.cs @@ -239,7 +239,7 @@ public static IRoute CreateEndpointRoute : segmentAttributesMapping; var endpointRoute = (intermediateSegments ?? []) - .Concat(segmentAttributesMapping.Select(x => x.Segment)); + .Concat(segmentAttributesMapping.Where(x => !x.Segment.IsEmpty).Select(x => x.Segment)); var classSegment = !className.StartsWith(_indexPrefix) ? segment?.ToPathSegment() ?? new UriPathSegmentConstant(className) diff --git a/src/WebExpress.WebCore/WebPackage/PackageManager.cs b/src/WebExpress.WebCore/WebPackage/PackageManager.cs index d7f7cda..8571f43 100644 --- a/src/WebExpress.WebCore/WebPackage/PackageManager.cs +++ b/src/WebExpress.WebCore/WebPackage/PackageManager.cs @@ -316,6 +316,12 @@ private void LoadCatalog() if (File.Exists(catalogeFile)) { using var catalog = new StreamReader(catalogeFile); + + if (catalog.BaseStream.Length == 0) + { + return; + } + var serializer = new XmlSerializer(typeof(PackageCatalog)); var items = (PackageCatalog)serializer.Deserialize(catalog); From 01db45f067526976013b3b7cb112599faf3c3369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:40:56 +0200 Subject: [PATCH 152/162] Update src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs index abcd0ef..b263ac9 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs @@ -31,8 +31,7 @@ internal class MockIdentityGroup : IIdentityGroup /// The name of the group. public MockIdentityGroup(Guid id, string name) { - Id = id; ; - Name = name; + Id = id; } /// From 73eb82e19dcd510712ba3a22afbf306d1abf637c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:41:22 +0200 Subject: [PATCH 153/162] Update src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index a275199..d58a937 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -191,11 +191,12 @@ public static RenderContext CrerateRenderContextMock(IApplicationContext applica var request = CrerateRequestMock(); return new RenderContext(null, CreratePageContextMock(applicationContext, scopes), request); - } + } + /// /// Create a fake page context for unit testing. /// - /// The application context. If null, defaults to null. + /// The scopes of the page. /// The scopes of the page. /// A fake context for testing. public static PageContext CreratePageContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) From ba20eb0bd3e00d3255a6fc1de5e48515baa2a135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:41:32 +0200 Subject: [PATCH 154/162] Update src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs index 68a9b71..9fef8c1 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs @@ -56,7 +56,7 @@ private static IEnumerable CreateTestGroups() /// /// Creates a list of test users with mock identities. /// - /// A list of identitys. + /// A list of identities. private static IEnumerable CreateTestUsers() { var passwort = new SecureString(); From 2b6e582c95163120fa409b5ced7f29b34ead27b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:46:20 +0200 Subject: [PATCH 155/162] Update src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs index d58a937..31d7728 100644 --- a/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs +++ b/src/WebExpress.WebCore.Test/Fixture/UnitTestFixture.cs @@ -197,7 +197,6 @@ public static RenderContext CrerateRenderContextMock(IApplicationContext applica /// Create a fake page context for unit testing. /// /// The scopes of the page. - /// The scopes of the page. /// A fake context for testing. public static PageContext CreratePageContextMock(IApplicationContext applicationContext = null, IEnumerable scopes = null) { From 3fcf8246c92f555ecd334f0eda622d183311ceed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:48:52 +0200 Subject: [PATCH 156/162] Update src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs index b263ac9..e566770 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityGroup.cs @@ -32,6 +32,7 @@ internal class MockIdentityGroup : IIdentityGroup public MockIdentityGroup(Guid id, string name) { Id = id; + Name = name; } /// From 6c4bcb82c568ea9cd38079cc103687b7fdf050fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:49:17 +0200 Subject: [PATCH 157/162] Update src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Data/MockIdentityFactory.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs index 9fef8c1..119b04f 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs @@ -59,23 +59,23 @@ private static IEnumerable CreateTestGroups() /// A list of identities. private static IEnumerable CreateTestUsers() { - var passwort = new SecureString(); - passwort.AppendChar('a'); - passwort.AppendChar('b'); - passwort.AppendChar('c'); - passwort.MakeReadOnly(); + var password = new SecureString(); + password.AppendChar('a'); + password.AppendChar('b'); + password.AppendChar('c'); + password.MakeReadOnly(); - var user = new MockIdentity(Guid.NewGuid(), "Alice", "alice@example.com", IdentityManager.ComputeHash(passwort)); + var user = new MockIdentity(Guid.NewGuid(), "Alice", "alice@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(0)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Bob", "bob@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Bob", "bob@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Charlie", "charlie@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Charlie", "charlie@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(2)]); yield return user; From 0194d0ae9b919612fc1512639cd65958492280fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:49:48 +0200 Subject: [PATCH 158/162] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 5e7e6bf..70220e9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ ![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) # WebExpress -`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Rasperry PI). By providing -a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net +`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing language (e.g. C#). Some advantages of `WebExpress` are: - It is easy to use. From 81032c1ba855e4145f1311c6e1afe27ccb009099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:50:07 +0200 Subject: [PATCH 159/162] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 70220e9..8288156 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The `WebExpress` family includes the following projects: - [WebExpress.WebApp](https://github.com/ReneSchwarzer/WebExpress.WebApp#readme) - Business application template for `WebExpress` applications. # WebExpress.WebCore -`WebCore` is part of the `WebExpres` family and includes the basic elements of a `WebExpress` application. +`WebCore` is part of the `WebExpress` family and includes the basic elements of a `WebExpress` application. # Download The current binaries are available for download [here](https://github.com/ReneSchwarzer/WebExpress/releases). From d9c879b93342f4bbeef73e4686a27fbecf49f627 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 18 Apr 2025 13:11:52 +0200 Subject: [PATCH 160/162] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8288156..5390470 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ ![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) # WebExpress -`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing -language (e.g. C#). Some advantages of `WebExpress` are: +`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net language (e.g. C#). Some dvantages of `WebExpress` are: - It is easy to use. - It offers a variety of features and tools that can help you build and manage your website. From cebf506877f0a1a8169ab595394febde8d847421 Mon Sep 17 00:00:00 2001 From: Rene Schwarzer Date: Fri, 18 Apr 2025 13:12:48 +0200 Subject: [PATCH 161/162] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5390470..100ea8f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![WebExpress](https://raw.githubusercontent.com/ReneSchwarzer/WebExpress/main/assets/banner.png) # WebExpress -`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net language (e.g. C#). Some dvantages of `WebExpress` are: +`WebExpress` is a lightweight web server optimized for use in low-performance environments (e.g. Raspberry PI). By providing a powerful plugin system and a comprehensive API, web applications can be easily and quickly integrated into a .net language (e.g. C#). Some advantages of `WebExpress` are: - It is easy to use. - It offers a variety of features and tools that can help you build and manage your website. From c207bdd7bc9d50df6b00d9daaa24e354b5022801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Schwarzer?= <31061438+ReneSchwarzer@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:13:30 +0200 Subject: [PATCH 162/162] Update src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Data/MockIdentityFactory.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs index 119b04f..cd2b9ee 100644 --- a/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs +++ b/src/WebExpress.WebCore.Test/Data/MockIdentityFactory.cs @@ -80,37 +80,37 @@ private static IEnumerable CreateTestUsers() yield return user; - user = new MockIdentity(Guid.NewGuid(), "David", "david@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "David", "david@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1), _groups.ElementAt(2)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Eve", "eve@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Eve", "eve@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Frank", "frank@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Frank", "frank@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Grace", "grace@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Grace", "grace@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Heidi", "heidi@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Heidi", "heidi@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Ivan", "ivan@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Ivan", "ivan@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user; - user = new MockIdentity(Guid.NewGuid(), "Judy", "judy@example.com", IdentityManager.ComputeHash(passwort)); + user = new MockIdentity(Guid.NewGuid(), "Judy", "judy@example.com", IdentityManager.ComputeHash(password)); user.Assign([_groups.ElementAt(1)]); yield return user;