diff --git a/.gitignore b/.gitignore index a7e79f2..42546a1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ /Voile.Storage.Postgresql/bin/Debug/net8.0 /Voile.Storage.SqlServer/bin/Debug/net8.0 /Voile.Storage.Sqlite/obj +/Voile.Storage.Sqlite/bin/Debug/net8.0 diff --git a/Voile.Patchouli/Reflection/VoilePluginManager.cs b/Voile.Patchouli/Reflection/VoilePluginManager.cs index 16c40fc..73e8ffb 100644 --- a/Voile.Patchouli/Reflection/VoilePluginManager.cs +++ b/Voile.Patchouli/Reflection/VoilePluginManager.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using Voile.Common.Logging; using Voile.Common.Reflection; using Voile.Patchouli.Data; @@ -16,16 +17,19 @@ namespace Voile.Patchouli.Reflection public static VoilePluginManager _instance; private static Type _dataStorageFactoryType = typeof(IVoileDataStorageFactory); private static Type _objectStorageFactoryType = typeof(IVoileObjectStorageFactory); - - public static VoilePluginManager GetInstance() + private static VoileLogger logger; + + public static VoilePluginManager GetInstance() { if (_instance == null) { _instance = new VoilePluginManager(); + logger = VoileLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } return _instance; } + private List _loadedAssemblies; private List _knownDataStorages; private List _knownObjectStorages; @@ -37,6 +41,8 @@ namespace Voile.Patchouli.Reflection if (_loadedAssemblies.Contains(assembly)) return false; + logger.Info("Scanning assembly: {0}", assembly.GetName().Name); + bool result = false; Type[] types = assembly.GetTypes(); @@ -86,7 +92,20 @@ namespace Voile.Patchouli.Reflection public IReadOnlyList GetDataStorages() { + if (_knownDataStorages == null) + { + return new List(); + } return _knownDataStorages.AsReadOnly(); } + + public DirectoryInfo GetVoileHomeDirectory() + { + Assembly? assembly = Assembly.GetEntryAssembly(); + string location = assembly.Location; + FileInfo locationFi = new FileInfo(location); + DirectoryInfo directory = locationFi.Directory; + return directory; + } } } diff --git a/Voile.Storage.Oracle/OracleDataStorageFactory.cs b/Voile.Storage.Oracle/OracleDataStorageFactory.cs new file mode 100644 index 0000000..e87d281 --- /dev/null +++ b/Voile.Storage.Oracle/OracleDataStorageFactory.cs @@ -0,0 +1,47 @@ +using Oracle.ManagedDataAccess.Client; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voile.Common.DRM; +using Voile.Common.Reflection; +using Voile.Patchouli.Data; +using Voile.Patchouli.Reflection; + +namespace Voile.Storage.Oracle +{ + [VoilePlugin] + [VoilePluginId(2)] + [NeedsEntitlement("{D48BDD34-7D5B-4543-BFA7-71A8C763A298}")] + internal class OracleDataStorageFactory : IVoileDataStorageFactory + { + public OracleDataStorageFactory() + { + } + + public IVoileDataStorage CreateDataStorage() + { + throw new NotImplementedException(); + } + + private OracleConnectionStringBuilder ocsb; + + + [Description("The IP-Address or Hostname the TNS-Listener in running on.")] + public string Host { get; set; } + + [Description("The Port on which TNS-Listener is listening on.")] + public ushort Port { get; set; } + + [Description("The Service Name registered with TNS-Listener. Usually, this is the name of your Pluggable Database. Note that this is not the SID!")] + public string Service { get; set; } + + [Description("The username to login with")] + public string User { get; set; } + + [Description("The password to login with")] + public string Password { get; set; } + } +} diff --git a/Voile.Storage.Oracle/Voile.Storage.Oracle.csproj b/Voile.Storage.Oracle/Voile.Storage.Oracle.csproj index fa71b7a..a25024f 100644 --- a/Voile.Storage.Oracle/Voile.Storage.Oracle.csproj +++ b/Voile.Storage.Oracle/Voile.Storage.Oracle.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + diff --git a/Voile.Storage.Postgresql/PostgresqlDataStorageFactory.cs b/Voile.Storage.Postgresql/PostgresqlDataStorageFactory.cs new file mode 100644 index 0000000..f172158 --- /dev/null +++ b/Voile.Storage.Postgresql/PostgresqlDataStorageFactory.cs @@ -0,0 +1,40 @@ +using Npgsql; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voile.Common.Reflection; +using Voile.Patchouli.Data; +using Voile.Patchouli.Reflection; + +namespace Voile.Storage.Postgresql +{ + [VoilePlugin] + [VoilePluginId(4)] + internal class PostgresqlDataStorageFactory : IVoileDataStorageFactory + { + public IVoileDataStorage CreateDataStorage() + { + throw new NotImplementedException(); + } + + private NpgsqlConnectionStringBuilder ncsb; + + [Description("The IP Adress or Hostname on which PostgreSQL is running.")] + public string Host { get; set; } + + [Description("The Port number on which PostgreSQL is listening on.")] + public ushort Port { get; set; } + + [Description("The username to login with.")] + public string Username { get; set; } + + [Description("The password to login with.")] + public string Password { get; set; } + + [Description("The database inside PostgreSQL to connect to")] + public string DatabaseName { get; set; } + } +} diff --git a/Voile.Storage.Postgresql/Voile.Storage.Postgresql.csproj b/Voile.Storage.Postgresql/Voile.Storage.Postgresql.csproj index fa71b7a..df8ce16 100644 --- a/Voile.Storage.Postgresql/Voile.Storage.Postgresql.csproj +++ b/Voile.Storage.Postgresql/Voile.Storage.Postgresql.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + diff --git a/Voile.Storage.SqlServer/README.md b/Voile.Storage.SqlServer/README.md index eb3bf81..d66f63b 100644 --- a/Voile.Storage.SqlServer/README.md +++ b/Voile.Storage.SqlServer/README.md @@ -36,5 +36,5 @@ GO 3. Start the container: -`docker run --rm -it -e "MSSQL_SA_PASSWORD=Voile12345" --memory=1G --name sqlserver -e "ACCEPT_EULA=Y" -v /data/sqlserver:/var/opt/mssql/data -p 1433:1433 mcr.microsoft.com/mssql/server:2025-latest` +`docker run --rm -it -e "MSSQL_PID=Express" -e "MSSQL_SA_PASSWORD=Voile12345" --memory=1G --name sqlserver -e "ACCEPT_EULA=Y" -v /data/sqlserver:/var/opt/mssql/data -p 1433:1433 mcr.microsoft.com/mssql/server:2025-latest` diff --git a/Voile.Storage.SqlServer/SqlServerDataStorageFactory.cs b/Voile.Storage.SqlServer/SqlServerDataStorageFactory.cs new file mode 100644 index 0000000..ae3b0cb --- /dev/null +++ b/Voile.Storage.SqlServer/SqlServerDataStorageFactory.cs @@ -0,0 +1,42 @@ +using Microsoft.Data.SqlClient; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voile.Common.DRM; +using Voile.Common.Reflection; +using Voile.Patchouli.Data; +using Voile.Patchouli.Reflection; + +namespace Voile.Storage.SqlServer +{ + [VoilePlugin] + [VoilePluginId(5)] + [NeedsEntitlement("{95EA2C4C-259F-4A26-A2A6-1F17ED2A397A}")] + internal class SqlServerDataStorageFactory : IVoileDataStorageFactory + { + private SqlConnectionStringBuilder scsb; + + [Description("The host SQL Server is running on.")] + public string Host { get; set; } + + [Description("The port SQL Server is listening on.")] + public ushort Port { get; set; } + + [Description("The username to login with.")] + public string Username { get; set; } + + [Description("The password to login with.")] + public string Password { get; set; } + + [Description("The SQL Server database to connect with")] + public string Database { get; set; } + + public IVoileDataStorage CreateDataStorage() + { + throw new NotImplementedException(); + } + } +} diff --git a/Voile.Storage.SqlServer/Voile.Storage.SqlServer.csproj b/Voile.Storage.SqlServer/Voile.Storage.SqlServer.csproj index fa71b7a..8525051 100644 --- a/Voile.Storage.SqlServer/Voile.Storage.SqlServer.csproj +++ b/Voile.Storage.SqlServer/Voile.Storage.SqlServer.csproj @@ -6,4 +6,12 @@ enable + + + + + + + + diff --git a/Voile.Storage.Sqlite/SqliteDataStorageFactory.cs b/Voile.Storage.Sqlite/SqliteDataStorageFactory.cs new file mode 100644 index 0000000..71d41a0 --- /dev/null +++ b/Voile.Storage.Sqlite/SqliteDataStorageFactory.cs @@ -0,0 +1,26 @@ +using Microsoft.Data.Sqlite; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voile.Common.Reflection; +using Voile.Patchouli.Data; +using Voile.Patchouli.Reflection; + +namespace Voile.Storage.Sqlite +{ + [VoilePlugin] + [VoilePluginId(3)] + public class SqliteDataStorageFactory : IVoileDataStorageFactory + { + public IVoileDataStorage CreateDataStorage() + { + throw new NotImplementedException(); + } + + public string FilePath { get; set; } + + private SqliteConnectionStringBuilder scsb; + } +} diff --git a/Voile.Storage.Sqlite/Voile.Storage.Sqlite.csproj b/Voile.Storage.Sqlite/Voile.Storage.Sqlite.csproj index e6acd33..9c3c3b5 100644 --- a/Voile.Storage.Sqlite/Voile.Storage.Sqlite.csproj +++ b/Voile.Storage.Sqlite/Voile.Storage.Sqlite.csproj @@ -6,6 +6,10 @@ enable + + + + diff --git a/Voile/DockContents/DockLog.cs b/Voile/DockContents/DockLog.cs index b5de291..09adf31 100644 --- a/Voile/DockContents/DockLog.cs +++ b/Voile/DockContents/DockLog.cs @@ -9,13 +9,16 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; +using Voile.Common.Logging; +using Voile.Common.Logging.Sinks; using WeifenLuo.WinFormsUI.Docking; namespace Voile.DockContents { - public partial class DockLog : DockContent, IAppender + public partial class DockLog : DockContent, IVoileLogSink { - private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static VoileLog4netSink log4netSink; + private static VoileLogger logger; public DockLog() { InitializeComponent(); @@ -23,6 +26,12 @@ namespace Voile.DockContents richTextBox1.AllowDrop = true; richTextBox1.DragEnter += RichTextBox1_DragEnter; richTextBox1.DragDrop += RichTextBox1_DragDrop; + + if (logger == null) + { + logger = VoileLogManager.GetLogger(this.GetType()); + logger.Debug("Initalize DockLog"); + } } public void Logger(string s) @@ -34,6 +43,26 @@ namespace Voile.DockContents richTextBox1.ResumeLayout(); } + public void OnLogMessage(VoileLogMessage message) + { + Logger(String.Format("{0} {1} {2} - {3}", message.Timestamp, message.Level, message.SourceName, message.Message)); + } + + private bool initalized; + internal void InitalizeLogging() + { + if (initalized) + { + throw new Voile.Common.VoileException("Logging already initalized!"); + } + + log4netSink = new VoileLog4netSink(); + log4netSink.Initalize(); + VoileLogManager.GetInstance().AddSink(this); + + initalized = true; + } + private void RichTextBox1_DragDrop(object? sender, DragEventArgs e) { } @@ -42,30 +71,6 @@ namespace Voile.DockContents { } - bool wasInitalized; - public void InitalizeLogging() - { - if (wasInitalized) - throw new Exception("already initalized"); - - var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository(); - hierarchy.Root.AddAppender(this); - hierarchy.Root.Level = log4net.Core.Level.All; - log4net.Config.BasicConfigurator.Configure(hierarchy); - wasInitalized = true; - } - - private log4net.Layout.PatternLayout logLayout; - public void DoAppend(LoggingEvent loggingEvent) - { - if (logLayout == null) - { - logLayout = new log4net.Layout.PatternLayout(); - logLayout.ConversionPattern = "%date %level %logger - %message"; - logLayout.ActivateOptions(); - } - string v = logLayout.Format(loggingEvent); - Logger(v); - } + } } diff --git a/Voile/FirstRunWizard.cs b/Voile/FirstRunWizard.cs index 6705a9a..c6bdc57 100644 --- a/Voile/FirstRunWizard.cs +++ b/Voile/FirstRunWizard.cs @@ -160,6 +160,17 @@ namespace Voile { DisplayTabPage(1); } + else if (currentTabPage == 1) + { + if (beginnerMode) + { + //TODO: Automatisch SQLite und Directory Storage auswählen + } + else + { + DisplayTabPage(2); + } + } } private void buttonLoadLicenseFile_Click(object sender, EventArgs e) diff --git a/Voile/Form1.cs b/Voile/Form1.cs index cb84f43..0ecd09b 100644 --- a/Voile/Form1.cs +++ b/Voile/Form1.cs @@ -1,3 +1,4 @@ +using Voile.Common.Logging; using Voile.DockContents; using Voile.Tasks; using WeifenLuo.WinFormsUI.Docking; @@ -6,11 +7,15 @@ namespace Voile { public partial class Form1 : Form { - private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static VoileLogger logger; public Form1() { InitializeComponent(); + if (logger == null) + { + logger = VoileLogManager.GetLogger(this.GetType()); + } dockPanel1.Theme = vS2003Theme1; logger.Info("Main Form initalized."); diff --git a/Voile/Persistence/DirectoryStorageFactory.cs b/Voile/Persistence/DirectoryStorageFactory.cs index 131d91c..722c774 100644 --- a/Voile/Persistence/DirectoryStorageFactory.cs +++ b/Voile/Persistence/DirectoryStorageFactory.cs @@ -18,7 +18,7 @@ namespace Voile.Persistence { public override bool Equals(object? obj) { - DirectoryStorageFactory dsf = obj as DirectoryStorageFactory + DirectoryStorageFactory dsf = obj as DirectoryStorageFactory; if (dsf == null) return false; diff --git a/Voile/Program.cs b/Voile/Program.cs index 623455d..43663e2 100644 --- a/Voile/Program.cs +++ b/Voile/Program.cs @@ -1,15 +1,24 @@ +using log4net; +using Voile.Common.Logging; +using Voile.Common.Logging.Sinks; + [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config")] namespace Voile { internal static class Program { - private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static VoileLogger logger; /// /// The main entry point for the application. /// [STAThread] static void Main() { + VoileLogManager.GetInstance().AddSink(VoileConsoleSink.GetInstance()); + if (logger == null) + { + logger = VoileLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + } logger.Info("Hello! This is Voile, an UI to skyscraper8."); // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. diff --git a/Voile/README.md b/Voile/README.md index 02efcfc..8baec48 100644 --- a/Voile/README.md +++ b/Voile/README.md @@ -1,8 +1,12 @@ # DataStorage Ids -|Id|Name | -|--|----------------| -|1 |DirectoryStorage| +|Id|Name |Entitlement | +|--|----------------|-----------------------------------------| +|1 |DirectoryStorage| | +|2 |Oracle |\{D48BDD34-7D5B-4543-BFA7-71A8C763A298\} | +|3 |SQLite | | +|4 |PostgreSQL | | +|5 |SQL Server |\{95EA2C4C-259F-4A26-A2A6-1F17ED2A397A\} | # ObjectStorage Ids diff --git a/Voile/Tasks/StartupInitializeDatabase.cs b/Voile/Tasks/StartupInitializeDatabase.cs index 5fc1eed..e0c6885 100644 --- a/Voile/Tasks/StartupInitializeDatabase.cs +++ b/Voile/Tasks/StartupInitializeDatabase.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Voile.Patchouli.Reflection; +using Voile.Storage.Sqlite; namespace Voile.Tasks { @@ -12,7 +14,11 @@ namespace Voile.Tasks public Form1 Form1 { get; internal set; } private Ini ini; public void Run() - { + { + VoilePluginManager voilePluginManager = VoilePluginManager.GetInstance(); + voilePluginManager.ScanAssembly(this.GetType().Assembly); + voilePluginManager.ScanAssembly(typeof(SqliteDataStorageFactory).Assembly); + if (IsFirstRun()) { FirstRunWizard frw = new FirstRunWizard(); @@ -31,7 +37,9 @@ namespace Voile.Tasks return true; } - voileContext.Ini = new Ini(fileInfo.FullName); + voileContext.Ini = new Ini(fileInfo.FullName); + + bool firstRunComplete = ini.ReadValue("voile", "firstRunComplete", false); if (!firstRunComplete) diff --git a/Voile/Voile.csproj b/Voile/Voile.csproj index a3438ae..bed6cb0 100644 --- a/Voile/Voile.csproj +++ b/Voile/Voile.csproj @@ -23,6 +23,7 @@ + diff --git a/Voile/VoileContext.cs b/Voile/VoileContext.cs index 7987cdc..b4d1471 100644 --- a/Voile/VoileContext.cs +++ b/Voile/VoileContext.cs @@ -49,5 +49,6 @@ namespace Voile public Ini Ini { get; set; } public X509Certificate SophiaNetRootCertificate { get; internal set; } + public bool PluginManagerInitalized { get; set; } } } diff --git a/Voile/VoileLog4netBridge.cs b/Voile/VoileLog4netBridge.cs new file mode 100644 index 0000000..80fb28e --- /dev/null +++ b/Voile/VoileLog4netBridge.cs @@ -0,0 +1,78 @@ +using log4net.Appender; +using log4net.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Voile.Common; +using Voile.Common.Logging; + +namespace Voile +{ + internal class VoileLog4netSink : IAppender + { + private VoileLogger myOwnLogger; + private bool wasInitalized; + private bool isClosed; + public void Initalize() + { + if (wasInitalized) + { + throw new VoileException("Log sink already initalized."); + } + + myOwnLogger = VoileLogManager.GetLogger(GetType()); + myOwnLogger.Debug("Initalize VoileLog4netSink"); + + var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository(); + hierarchy.Root.AddAppender(this); + hierarchy.Root.Level = log4net.Core.Level.All; + log4net.Config.BasicConfigurator.Configure(hierarchy); + wasInitalized = true; + } + + private log4net.Layout.PatternLayout logLayout; + + private string log4netName; + public string Name { get => log4netName; set => log4netName = value; } + + public void DoAppend(LoggingEvent loggingEvent) + { + if (isClosed) + { + throw new VoileException("Can't append to a closed logger."); + } + if (logLayout == null) + { + logLayout = new log4net.Layout.PatternLayout(); + logLayout.ConversionPattern = "%date %level %logger - %message"; + logLayout.ActivateOptions(); + } + string v = logLayout.Format(loggingEvent); + + string loggerName = loggingEvent.LoggerName; + VoileLogLevel logLevel = TranslateLogLevel(loggingEvent.Level); + + VoileLogManager.GetLogger(loggerName).Log(logLevel, loggingEvent.RenderedMessage); + } + + private VoileLogLevel TranslateLogLevel(Level level) + { + switch(level.Value) + { + case 30000: + return VoileLogLevel.DEBUG; + case 40000: + return VoileLogLevel.INFO; + default: + throw new NotImplementedException(String.Format("Loglevel: {0}", level.Value)); + } + } + + public void Close() + { + isClosed = true; + } + } +}