Bläddra i källkod

Added global poster settings

Kenric Nugteren 1 år sedan
förälder
incheckning
17a40d8a1f

+ 23 - 0
InABox.Core/Postable/IPoster.cs

@@ -18,4 +18,27 @@ namespace InABox.Core
     {
         TSettings Settings { get; set; }
     }
+
+    public interface IGlobalSettingsPoster
+    {
+        public IGlobalPosterSettings GlobalSettings { get; set; }
+    }
+
+    public interface IGlobalSettingsPoster<TGlobalSettings> : IGlobalSettingsPoster
+        where TGlobalSettings : BaseObject, IGlobalPosterSettings
+    {
+        public new TGlobalSettings GlobalSettings { get; set; }
+
+        IGlobalPosterSettings IGlobalSettingsPoster.GlobalSettings
+        {
+            get => GlobalSettings;
+            set
+            {
+                if(value is TGlobalSettings settings)
+                {
+                    GlobalSettings = settings;
+                }
+            }
+        }
+    }
 }

+ 23 - 0
InABox.Core/Postable/PosterEngine.cs

@@ -9,6 +9,23 @@ using System.Text;
 
 namespace InABox.Core
 {
+    public interface IGlobalSettingsPosterEngine
+    {
+        IGlobalPosterSettings GetGlobalSettings();
+    }
+
+    /// <summary>
+    /// Marks an <see cref="IPosterEngine{TPostable}"/> as having global settings, not specific to certain entities.
+    /// </summary>
+    /// <typeparam name="TGlobalSettings"></typeparam>
+    public interface IGlobalSettingsPosterEngine<TPoster, TGlobalSettings> : IGlobalSettingsPosterEngine
+        where TPoster : IGlobalSettingsPoster<TGlobalSettings>
+        where TGlobalSettings : BaseObject, IGlobalPosterSettings, new()
+    {
+        new TGlobalSettings GetGlobalSettings() => PosterUtils.LoadGlobalPosterSettings<TGlobalSettings>();
+
+        IGlobalPosterSettings IGlobalSettingsPosterEngine.GetGlobalSettings() => GetGlobalSettings();
+    }
 
     public interface IPosterEngine<TPostable>
         where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
@@ -66,6 +83,12 @@ namespace InABox.Core
         {
             var poster = (Activator.CreateInstance(PosterType!) as TPoster)!;
             poster.Settings = GetSettings();
+
+            if(this is IGlobalSettingsPosterEngine globalSettingsEngine && poster is IGlobalSettingsPoster globalSettingsPoster)
+            {
+                globalSettingsPoster.GlobalSettings = globalSettingsEngine.GetGlobalSettings();
+            }
+
             return poster;
         }
 

+ 7 - 0
InABox.Core/Postable/PosterSettings.cs

@@ -27,4 +27,11 @@ namespace InABox.Core
             return DefaultScript(typeof(TPostable));
         }
     }
+
+    public interface IGlobalPosterSettings : IGlobalConfigurationSettings
+    {
+    }
+    public abstract class GlobalPosterSettings : BaseObject, IGlobalPosterSettings
+    {
+    }
 }

+ 40 - 1
InABox.Core/Postable/PosterUtils.cs

@@ -9,7 +9,6 @@ using System.Text;
 
 namespace InABox.Core
 {
-
     public static class PosterUtils
     {
         private class EngineType
@@ -52,6 +51,8 @@ namespace InABox.Core
             return _posterEngines;
         }
 
+        #region Postable Settings
+
         private static PostableSettings FixPostableSettings<TPostable>(PostableSettings settings)
             where TPostable : Entity, IPostable
         {
@@ -74,6 +75,10 @@ namespace InABox.Core
             new GlobalConfiguration<PostableSettings>(typeof(T).Name).Save(FixPostableSettings<T>(settings));
         }
 
+        #endregion
+
+        #region Poster Settings
+
         private static TSettings FixPosterSettings<TPostable, TSettings>(TSettings settings)
             where TPostable : IPostable
             where TSettings : PosterSettings, new()
@@ -116,6 +121,40 @@ namespace InABox.Core
             _savePosterSettingsMethod.MakeGenericMethod(TPostable, TSettings).Invoke(null, new object[] { settings });
         }
 
+        #endregion
+
+        #region Global Poster Settings
+
+        private static MethodInfo _loadGlobalPosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public)
+            .Where(x => x.Name == nameof(LoadGlobalPosterSettings) && x.IsGenericMethod)
+            .Single();
+        private static MethodInfo _saveGlobalPosterSettingsMethod = typeof(PosterUtils).GetMethods(BindingFlags.Static | BindingFlags.Public)
+            .Where(x => x.Name == nameof(SaveGlobalPosterSettings) && x.IsGenericMethod)
+            .Single();
+
+        public static IGlobalPosterSettings LoadGlobalPosterSettings(Type TGlobalSettings)
+        {
+            return (_loadGlobalPosterSettingsMethod.MakeGenericMethod(TGlobalSettings).Invoke(null, Array.Empty<object>()) as IGlobalPosterSettings)!;
+        }
+        public static TGlobalSettings LoadGlobalPosterSettings<TGlobalSettings>()
+            where TGlobalSettings : BaseObject, IGlobalPosterSettings, new()
+        {
+            return new GlobalConfiguration<TGlobalSettings>().Load();
+        }
+
+        public static void SaveGlobalPosterSettings<TGlobalSettings>(TGlobalSettings settings)
+            where TGlobalSettings : BaseObject, IGlobalPosterSettings, new()
+        {
+            new GlobalConfiguration<TGlobalSettings>().Save(settings);
+        }
+
+        public static void SaveGlobalPosterSettings(Type TGlobalSettings, IGlobalPosterSettings settings)
+        {
+            _saveGlobalPosterSettingsMethod.MakeGenericMethod(TGlobalSettings).Invoke(null, new object[] { settings });
+        }
+
+        #endregion
+
         /// <summary>
         /// Get the <see cref="IPosterEngine{TPostable,TPoster,TSettings}"/> for <typeparamref name="T"/>
         /// based on the current <see cref="PostableSettings"/> for <typeparamref name="T"/>.

+ 80 - 81
InABox.Poster.CSV/CSVPosterEngine.cs

@@ -10,109 +10,108 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.CSV
+namespace InABox.Poster.CSV;
+
+public class CSVPosterEngine<TPostable> : PosterEngine<TPostable, ICSVPoster<TPostable>, CSVPosterSettings>
+    where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
 {
-    public class CSVPosterEngine<TPostable> : PosterEngine<TPostable, ICSVPoster<TPostable>, CSVPosterSettings>
-        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
-    {
-        private ScriptDocument? _script;
-        private bool _hasCheckedScript;
+    private ScriptDocument? _script;
+    private bool _hasCheckedScript;
 
-        private ScriptDocument? GetScriptDocument()
+    private ScriptDocument? GetScriptDocument()
+    {
+        if (_hasCheckedScript)
         {
-            if (_hasCheckedScript)
-            {
-                return _script;
-            }
-
-            var settings = GetSettings();
-            if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
-            {
-                var document = new ScriptDocument(settings.Script);
-                document.Properties.Add(new ScriptProperty("Results", null));
-                if (!document.Compile())
-                {
-                    throw new Exception("Script failed to compile!");
-                }
-                _script = document;
-            }
-            else
-            {
-                _script = null;
-            }
-
-            _hasCheckedScript = true;
             return _script;
         }
 
-        public override bool BeforePost(IDataModel<TPostable> model)
+        var settings = GetSettings();
+        if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
         {
-            if(GetScriptDocument() is ScriptDocument script)
-            {
-                return script.Execute(methodname: "BeforePost", parameters: new object[] { model });
-            }
-            else
+            var document = new ScriptDocument(settings.Script);
+            document.Properties.Add(new ScriptProperty("Results", null));
+            if (!document.Compile())
             {
-                return Poster.BeforePost(model);
+                throw new Exception("Script failed to compile!");
             }
+            _script = document;
+        }
+        else
+        {
+            _script = null;
         }
 
-        protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
+        _hasCheckedScript = true;
+        return _script;
+    }
+
+    public override bool BeforePost(IDataModel<TPostable> model)
+    {
+        if(GetScriptDocument() is ScriptDocument script)
         {
-            var settings = GetSettings();
+            return script.Execute(methodname: "BeforePost", parameters: new object[] { model });
+        }
+        else
+        {
+            return Poster.BeforePost(model);
+        }
+    }
 
-            ICSVExport<TPostable> results;
-            if (GetScriptDocument() is ScriptDocument script)
-            {
-                if (!script.Execute(methodname: "Process", parameters: new object[] { model }))
-                {
-                    throw new Exception("Post Failed.");
-                }
+    protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
+    {
+        var settings = GetSettings();
 
-                var resultsObject = script.GetValue("Results");
-                results = (resultsObject as ICSVExport<TPostable>)
-                    ?? throw new Exception($"Script 'Results' property expected to be ICSVExport<{typeof(TPostable)}>, got {resultsObject}");
-            }
-            else
+        ICSVExport<TPostable> results;
+        if (GetScriptDocument() is ScriptDocument script)
+        {
+            if (!script.Execute(methodname: "Process", parameters: new object[] { model }))
             {
-                results = Poster.Process(model);
+                throw new Exception("Post Failed.");
             }
 
-            var dlg = new SaveFileDialog()
-            {
-                FileName = settings.DefaultOutputFile,
-                Filter = "CSV Files (*.csv)|*.csv"
-            };
+            var resultsObject = script.GetValue("Results");
+            results = (resultsObject as ICSVExport<TPostable>)
+                ?? throw new Exception($"Script 'Results' property expected to be ICSVExport<{typeof(TPostable)}>, got {resultsObject}");
+        }
+        else
+        {
+            results = Poster.Process(model);
+        }
 
-            if(dlg.ShowDialog() == true)
-            {
-                using var writer = new StreamWriter(dlg.FileName);
-                using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
-                csv.Context.RegisterClassMap(results.ClassMap.ClassMap);
+        var dlg = new SaveFileDialog()
+        {
+            FileName = settings.DefaultOutputFile,
+            Filter = "CSV Files (*.csv)|*.csv"
+        };
 
-                var method = typeof(CsvWriter).GetMethods()
-                    .Where(x => x.Name == nameof(CsvWriter.WriteRecords) && x.GetGenericArguments().Length == 1).First()
-                    .MakeGenericMethod(results.Type);
-                method.Invoke(csv, new object?[] { results.Records });
-                return results;
-            }
-            else
-            {
-                throw new PostCancelledException();
-            }
+        if(dlg.ShowDialog() == true)
+        {
+            using var writer = new StreamWriter(dlg.FileName);
+            using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
+            csv.Context.RegisterClassMap(results.ClassMap.ClassMap);
+
+            var method = typeof(CsvWriter).GetMethods()
+                .Where(x => x.Name == nameof(CsvWriter.WriteRecords) && x.GetGenericArguments().Length == 1).First()
+                .MakeGenericMethod(results.Type);
+            method.Invoke(csv, new object?[] { results.Records });
+            return results;
+        }
+        else
+        {
+            throw new PostCancelledException();
         }
+    }
 
-        public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
+    public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
+    {
+        if (GetScriptDocument() is ScriptDocument script)
         {
-            if (GetScriptDocument() is ScriptDocument script)
-            {
-                // Ignoring 'result', because this is actually the 'Results' field of the script class.
-                script.Execute(methodname: "AfterPost", parameters: new object[] { model });
-            }
-            else
-            {
-                Poster.AfterPost(model, result);
-            }
+            // Ignoring 'result', because this is actually the 'Results' field of the script class.
+            script.Execute(methodname: "AfterPost", parameters: new object[] { model });
+        }
+        else
+        {
+            Poster.AfterPost(model, result);
         }
     }
 }

+ 11 - 12
InABox.Poster.CSV/CSVPosterSettings.cs

@@ -5,21 +5,21 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.CSV
+namespace InABox.Poster.CSV;
+
+public class CSVPosterSettings : PosterSettings
 {
-    public class CSVPosterSettings : PosterSettings
-    {
-        [FileNameEditor("CSV Files (*.csv)|*.csv", RequireExisting = false)]
-        public string DefaultOutputFile { get; set; }
+    [FileNameEditor("CSV Files (*.csv)|*.csv", RequireExisting = false)]
+    public string DefaultOutputFile { get; set; }
 
-        public override string DefaultScript(Type TPostable)
-        {
-            var tName = TPostable.Name;
-            var decapital = tName[0..1].ToLower() + tName[1..];
+    public override string DefaultScript(Type TPostable)
+    {
+        var tName = TPostable.Name;
+        var decapital = tName[0..1].ToLower() + tName[1..];
 
-            var ns = TPostable.Namespace;
+        var ns = TPostable.Namespace;
 
-            return @"
+        return @"
 using " + ns + @";
 using InABox.Poster.CSV;
 using InABox.Core;
@@ -68,6 +68,5 @@ public class Module
     {
     }
 }";
-        }
     }
 }

+ 71 - 72
InABox.Poster.CSV/ICSVPoster.cs

@@ -4,103 +4,102 @@ using System.Collections.Generic;
 using System.Linq.Expressions;
 using System.Text;
 
-namespace InABox.Poster.CSV
+namespace InABox.Poster.CSV;
+
+public interface ICSVClassMap
 {
-    public interface ICSVClassMap
-    {
-        ClassMap ClassMap { get; }
-    }
+    ClassMap ClassMap { get; }
+}
 
-    public interface ICSVClassMap<T> : ICSVClassMap
-    {
-        new ClassMap<T> ClassMap { get; }
+public interface ICSVClassMap<T> : ICSVClassMap
+{
+    new ClassMap<T> ClassMap { get; }
 
-        ClassMap ICSVClassMap.ClassMap => ClassMap;
+    ClassMap ICSVClassMap.ClassMap => ClassMap;
 
-        void Map(string name, Expression<Func<T, object>> expr);
-    }
+    void Map(string name, Expression<Func<T, object>> expr);
+}
 
-    public class CSVClassMap<T> : ClassMap<T>, ICSVClassMap<T>
-    {
-        public ClassMap<T> ClassMap => this;
+public class CSVClassMap<T> : ClassMap<T>, ICSVClassMap<T>
+{
+    public ClassMap<T> ClassMap => this;
 
-        public void Map(string name, Expression<Func<T, object>> expr)
-        {
-            Map(expr).Name(name);
-        }
+    public void Map(string name, Expression<Func<T, object>> expr)
+    {
+        Map(expr).Name(name);
     }
+}
 
-    public interface ICSVExport<TPostable> : IPostResult<TPostable>
-        where TPostable : IPostable
-    {
-        Type Type { get; }
+public interface ICSVExport<TPostable> : IPostResult<TPostable>
+    where TPostable : IPostable
+{
+    Type Type { get; }
 
-        ICSVClassMap ClassMap { get; }
+    ICSVClassMap ClassMap { get; }
 
-        IEnumerable<object> Records { get; }
-    }
+    IEnumerable<object> Records { get; }
+}
 
-    public interface ICSVExport<TExport, TPostable> : ICSVExport<TPostable>
-        where TExport : class
-        where TPostable : IPostable
-    {
-        new Type Type => typeof(TExport);
-        new CSVClassMap<TExport> ClassMap { get; }
-        new IEnumerable<TExport> Records { get; }
+public interface ICSVExport<TExport, TPostable> : ICSVExport<TPostable>
+    where TExport : class
+    where TPostable : IPostable
+{
+    new Type Type => typeof(TExport);
+    new CSVClassMap<TExport> ClassMap { get; }
+    new IEnumerable<TExport> Records { get; }
 
-        Type ICSVExport<TPostable>.Type => Type;
-        ICSVClassMap ICSVExport<TPostable>.ClassMap => ClassMap;
-        IEnumerable<object> ICSVExport<TPostable>.Records => Records;
-    }
+    Type ICSVExport<TPostable>.Type => Type;
+    ICSVClassMap ICSVExport<TPostable>.ClassMap => ClassMap;
+    IEnumerable<object> ICSVExport<TPostable>.Records => Records;
+}
 
-    public class CSVExport<TExport, TPostable> : ICSVExport<TExport, TPostable>
-        where TExport : class
-        where TPostable : IPostable
-    {
-        public CSVClassMap<TExport> ClassMap { get; } = new CSVClassMap<TExport>();
+public class CSVExport<TExport, TPostable> : ICSVExport<TExport, TPostable>
+    where TExport : class
+    where TPostable : IPostable
+{
+    public CSVClassMap<TExport> ClassMap { get; } = new CSVClassMap<TExport>();
 
-        public IEnumerable<TExport> Records => items.Where(x => x.Item2.PostedStatus == PostedStatus.Posted).Select(x => x.Item1);
+    public IEnumerable<TExport> Records => items.Where(x => x.Item2.PostedStatus == PostedStatus.Posted).Select(x => x.Item1);
 
-        private List<Tuple<TExport, TPostable>> items = new List<Tuple<TExport, TPostable>>();
+    private List<Tuple<TExport, TPostable>> items = new List<Tuple<TExport, TPostable>>();
 
-        private Dictionary<Type, List<IPostableFragment<TPostable>>> fragments = new Dictionary<Type, List<IPostableFragment<TPostable>>>();
+    private Dictionary<Type, List<IPostableFragment<TPostable>>> fragments = new Dictionary<Type, List<IPostableFragment<TPostable>>>();
 
-        public IEnumerable<TPostable> PostedEntities => items.Select(x => x.Item2);
+    public IEnumerable<TPostable> PostedEntities => items.Select(x => x.Item2);
 
-        public IEnumerable<KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>> Fragments =>
-            fragments.Select(x => new KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>(x.Key, x.Value));
+    public IEnumerable<KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>> Fragments =>
+        fragments.Select(x => new KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>(x.Key, x.Value));
 
-        public void DefineMapping(List<Tuple<string, Expression<Func<TExport, object>>>> mappings)
-        {
-            foreach(var (name, expr) in mappings)
-            {
-                ClassMap.Map(name, expr);
-            }
-        }
-        public void Map(string name,  Expression<Func<TExport, object>> expr)
+    public void DefineMapping(List<Tuple<string, Expression<Func<TExport, object>>>> mappings)
+    {
+        foreach(var (name, expr) in mappings)
         {
             ClassMap.Map(name, expr);
         }
-
-        public void AddSuccess(TExport export, TPostable postable)
-        {
-            postable.Post();
-            items.Add(new(export, postable));
-        }
+    }
+    public void Map(string name,  Expression<Func<TExport, object>> expr)
+    {
+        ClassMap.Map(name, expr);
     }
 
-    /// <summary>
-    /// Defines an interface for posters that can export to CSV.
-    /// </summary>
-    /// <typeparam name="TEntity"></typeparam>
-    [Caption("CSV")]
-    public interface ICSVPoster<TEntity> : IPoster<TEntity, CSVPosterSettings>
-        where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
+    public void AddSuccess(TExport export, TPostable postable)
     {
-        bool BeforePost(IDataModel<TEntity> model);
+        postable.Post();
+        items.Add(new(export, postable));
+    }
+}
+
+/// <summary>
+/// Defines an interface for posters that can export to CSV.
+/// </summary>
+/// <typeparam name="TEntity"></typeparam>
+[Caption("CSV")]
+public interface ICSVPoster<TEntity> : IPoster<TEntity, CSVPosterSettings>
+    where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
+{
+    bool BeforePost(IDataModel<TEntity> model);
 
-        ICSVExport<TEntity> Process(IDataModel<TEntity> model);
+    ICSVExport<TEntity> Process(IDataModel<TEntity> model);
 
-        void AfterPost(IDataModel<TEntity> model, IPostResult<TEntity> result);
-    }
+    void AfterPost(IDataModel<TEntity> model, IPostResult<TEntity> result);
 }

+ 48 - 49
InABox.Poster.Custom/CustomPosterEngine.cs

@@ -6,73 +6,72 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Custom
+namespace InABox.Poster.Custom;
+
+public class CustomPosterEngine<TPostable> : PosterEngine<TPostable, ICustomPoster<TPostable>, CustomPosterSettings>
+    where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
 {
-    public class CustomPosterEngine<TPostable> : PosterEngine<TPostable, ICustomPoster<TPostable>, CustomPosterSettings>
-        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
-    {
-        private ScriptDocument? _script;
-        private bool _hasCheckedScript;
+    private ScriptDocument? _script;
+    private bool _hasCheckedScript;
 
-        private ScriptDocument? GetScriptDocument()
+    private ScriptDocument? GetScriptDocument()
+    {
+        if (_hasCheckedScript)
         {
-            if (_hasCheckedScript)
-            {
-                return _script;
-            }
-
-            var settings = GetSettings();
-            if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
-            {
-                var document = new ScriptDocument(settings.Script);
-                if (!document.Compile())
-                {
-                    throw new Exception("Script failed to compile!");
-                }
-                _script = document;
-            }
-            else
-            {
-                _script = null;
-            }
-
-            _hasCheckedScript = true;
             return _script;
         }
 
-        public override bool BeforePost(IDataModel<TPostable> model)
+        var settings = GetSettings();
+        if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
         {
-            if (GetScriptDocument() is ScriptDocument script)
+            var document = new ScriptDocument(settings.Script);
+            if (!document.Compile())
             {
-                return script.Execute(methodname: "BeforePost", parameters: new object[] { model });
+                throw new Exception("Script failed to compile!");
             }
-            return false;
+            _script = document;
         }
+        else
+        {
+            _script = null;
+        }
+
+        _hasCheckedScript = true;
+        return _script;
+    }
 
-        protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
+    public override bool BeforePost(IDataModel<TPostable> model)
+    {
+        if (GetScriptDocument() is ScriptDocument script)
         {
-            if (GetScriptDocument() is ScriptDocument script)
-            {
-                if(!script.Execute(methodname: "Process", parameters: new object[] { model }))
-                {
-                    throw new Exception("Post Failed.");
-                }
+            return script.Execute(methodname: "BeforePost", parameters: new object[] { model });
+        }
+        return false;
+    }
 
-                var resultsObject = script.GetValue("Results");
-                return (resultsObject as IPostResult<TPostable>)
-                    ?? throw new Exception($"Script 'Results' property expected to be IPostResult<{typeof(TPostable)}>, got {resultsObject}");
-            }
-            else
+    protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
+    {
+        if (GetScriptDocument() is ScriptDocument script)
+        {
+            if(!script.Execute(methodname: "Process", parameters: new object[] { model }))
             {
                 throw new Exception("Post Failed.");
             }
+
+            var resultsObject = script.GetValue("Results");
+            return (resultsObject as IPostResult<TPostable>)
+                ?? throw new Exception($"Script 'Results' property expected to be IPostResult<{typeof(TPostable)}>, got {resultsObject}");
         }
-        public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
+        else
         {
-            if (GetScriptDocument() is ScriptDocument script)
-            {
-                script.Execute(methodname: "AfterPost", parameters: new object[] { model });
-            }
+            throw new Exception("Post Failed.");
+        }
+    }
+    public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
+    {
+        if (GetScriptDocument() is ScriptDocument script)
+        {
+            script.Execute(methodname: "AfterPost", parameters: new object[] { model });
         }
     }
 }

+ 8 - 9
InABox.Poster.Custom/CustomPosterSettings.cs

@@ -5,18 +5,18 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Custom
+namespace InABox.Poster.Custom;
+
+public class CustomPosterSettings : PosterSettings
 {
-    public class CustomPosterSettings : PosterSettings
+    public override string DefaultScript(Type TPostable)
     {
-        public override string DefaultScript(Type TPostable)
-        {
-            var tName = TPostable.Name;
-            var decapital = tName[0..1].ToLower() + tName[1..];
+        var tName = TPostable.Name;
+        var decapital = tName[0..1].ToLower() + tName[1..];
 
-            var ns = TPostable.Namespace;
+        var ns = TPostable.Namespace;
 
-            return @"
+        return @"
 using " + ns + @";
 using InABox.Core;
 using System.Collections.Generic;
@@ -49,6 +49,5 @@ public class Module
     {
     }
 }";
-        }
     }
 }

+ 5 - 6
InABox.Poster.Custom/ICustomPoster.cs

@@ -1,10 +1,9 @@
 using InABox.Core;
 
-namespace InABox.Poster.Custom
+namespace InABox.Poster.Custom;
+
+[Caption("Custom")]
+public interface ICustomPoster<TEntity> : IPoster<TEntity, CustomPosterSettings>
+    where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
 {
-    [Caption("Custom")]
-    public interface ICustomPoster<TEntity> : IPoster<TEntity, CustomPosterSettings>
-        where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
-    {
-    }
 }

+ 10 - 11
InABox.Poster.Timberline/ITimberlinePoster.cs

@@ -5,19 +5,18 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Timberline
+namespace InABox.Poster.Timberline;
+
+[Caption("Timberline")]
+public interface ITimberlinePoster<TEntity, TSettings> : IPoster<TEntity, TSettings>
+    where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
+    where TSettings : TimberlinePosterSettings<TEntity>
 {
-    [Caption("Timberline")]
-    public interface ITimberlinePoster<TEntity, TSettings> : IPoster<TEntity, TSettings>
-        where TEntity : Entity, IPostable, IRemotable, IPersistent, new()
-        where TSettings : TimberlinePosterSettings<TEntity>
-    {
-        ScriptDocument? Script { set; }
+    ScriptDocument? Script { set; }
 
-        bool BeforePost(IDataModel<TEntity> model);
+    bool BeforePost(IDataModel<TEntity> model);
 
-        IPostResult<TEntity> Process(IDataModel<TEntity> model);
+    IPostResult<TEntity> Process(IDataModel<TEntity> model);
 
-        void AfterPost(IDataModel<TEntity> model, IPostResult<TEntity> result);
-    }
+    void AfterPost(IDataModel<TEntity> model, IPostResult<TEntity> result);
 }

+ 32 - 33
InABox.Poster.Timberline/TimberlineConverters.cs

@@ -7,54 +7,53 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Timberline
+namespace InABox.Poster.Timberline;
+
+public class TimberlinePosterStringConverter : DefaultTypeConverter
 {
-    public class TimberlinePosterStringConverter : DefaultTypeConverter
+    public int MaxLength { get; set; }
+
+    public TimberlinePosterStringConverter(int maxLength)
     {
-        public int MaxLength { get; set; }
+        MaxLength = maxLength;
+    }
 
-        public TimberlinePosterStringConverter(int maxLength)
-        {
-            MaxLength = maxLength;
-        }
+    public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
+    {
+        return text;
+    }
 
-        public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
+    public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
+    {
+        var str = value?.ToString() ?? "";
+        if (str.Length > MaxLength)
         {
-            return text;
+            str = str[..MaxLength];
         }
+        return str;
+    }
+}
 
-        public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
+public class TimberlinePosterDateConverter : DefaultTypeConverter
+{
+    public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
+    {
+        if (text.IsNullOrWhiteSpace())
         {
-            var str = value?.ToString() ?? "";
-            if (str.Length > MaxLength)
-            {
-                str = str[..MaxLength];
-            }
-            return str;
+            return DateTime.MinValue;
         }
+        return DateTime.Parse(text);
     }
 
-    public class TimberlinePosterDateConverter : DefaultTypeConverter
+    public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
     {
-        public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
+        if(value is DateTime date && date == DateTime.MinValue)
         {
-            if (text.IsNullOrWhiteSpace())
-            {
-                return DateTime.MinValue;
-            }
-            return DateTime.Parse(text);
+            return "";
         }
-
-        public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
+        else
         {
-            if(value is DateTime date && date == DateTime.MinValue)
-            {
-                return "";
-            }
-            else
-            {
-                return string.Format("{0:dd/MM/yyyy}", value);
-            }
+            return string.Format("{0:dd/MM/yyyy}", value);
         }
     }
 }

+ 30 - 31
InABox.Poster.Timberline/TimberlinePostResult.cs

@@ -5,47 +5,46 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Timberline
+namespace InABox.Poster.Timberline;
+
+public class TimberlinePostResult<TExport, TPostable> : IPostResult<TPostable>
+    where TPostable : IPostable
+    where TExport : class
 {
-    public class TimberlinePostResult<TExport, TPostable> : IPostResult<TPostable>
-        where TPostable : IPostable
-        where TExport : class
-    {
-        private List<Tuple<TPostable, TExport?>> items = new List<Tuple<TPostable, TExport?>>();
+    private List<Tuple<TPostable, TExport?>> items = new List<Tuple<TPostable, TExport?>>();
 
-        private Dictionary<Type, List<IPostableFragment<TPostable>>> fragments = new Dictionary<Type, List<IPostableFragment<TPostable>>>();
+    private Dictionary<Type, List<IPostableFragment<TPostable>>> fragments = new Dictionary<Type, List<IPostableFragment<TPostable>>>();
 
-        public IEnumerable<TExport> Exports => items
-            .Where(x => x.Item1.PostedStatus == PostedStatus.Posted)
-            .NotNull(x => x.Item2);
+    public IEnumerable<TExport> Exports => items
+        .Where(x => x.Item1.PostedStatus == PostedStatus.Posted)
+        .NotNull(x => x.Item2);
 
-        public IEnumerable<TPostable> PostedEntities => items.Select(x => x.Item1);
+    public IEnumerable<TPostable> PostedEntities => items.Select(x => x.Item1);
 
-        public IEnumerable<Tuple<TPostable, TExport?>> Items => items;
+    public IEnumerable<Tuple<TPostable, TExport?>> Items => items;
 
-        public IEnumerable<KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>> Fragments =>
-            fragments.Select(x => new KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>(x.Key, x.Value));
+    public IEnumerable<KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>> Fragments =>
+        fragments.Select(x => new KeyValuePair<Type, IEnumerable<IPostableFragment<TPostable>>>(x.Key, x.Value));
 
-        public void AddSuccess(TPostable post, TExport? export)
-        {
-            post.Post();
-            items.Add(new Tuple<TPostable, TExport?>(post, export));
-        }
+    public void AddSuccess(TPostable post, TExport? export)
+    {
+        post.Post();
+        items.Add(new Tuple<TPostable, TExport?>(post, export));
+    }
 
-        public void AddFailed(TPostable post, string note)
-        {
-            post.FailPost(note);
-            items.Add(new Tuple<TPostable, TExport?>(post, null));
-        }
+    public void AddFailed(TPostable post, string note)
+    {
+        post.FailPost(note);
+        items.Add(new Tuple<TPostable, TExport?>(post, null));
+    }
 
-        public void AddFragment(IPostableFragment<TPostable> fragment)
+    public void AddFragment(IPostableFragment<TPostable> fragment)
+    {
+        if (!fragments.TryGetValue(fragment.GetType(), out var fragmentList))
         {
-            if (!fragments.TryGetValue(fragment.GetType(), out var fragmentList))
-            {
-                fragmentList = new List<IPostableFragment<TPostable>>();
-                fragments[fragment.GetType()] = fragmentList;
-            }
-            fragmentList.Add(fragment);
+            fragmentList = new List<IPostableFragment<TPostable>>();
+            fragments[fragment.GetType()] = fragmentList;
         }
+        fragmentList.Add(fragment);
     }
 }

+ 42 - 43
InABox.Poster.Timberline/TimberlinePosterEngine.cs

@@ -8,61 +8,60 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Timberline
+namespace InABox.Poster.Timberline;
+
+public class TimberlinePosterEngine<TPostable, TSettings> : PosterEngine<TPostable, ITimberlinePoster<TPostable, TSettings>, TSettings>
+    where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
+    where TSettings : TimberlinePosterSettings<TPostable>, new()
 {
-    public class TimberlinePosterEngine<TPostable, TSettings> : PosterEngine<TPostable, ITimberlinePoster<TPostable, TSettings>, TSettings>
-        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()
-        where TSettings : TimberlinePosterSettings<TPostable>, new()
-    {
 
-        private ScriptDocument? _script;
-        private bool _hasCheckedScript;
+    private ScriptDocument? _script;
+    private bool _hasCheckedScript;
 
-        protected override ITimberlinePoster<TPostable, TSettings> CreatePoster()
+    protected override ITimberlinePoster<TPostable, TSettings> CreatePoster()
+    {
+        var poster = base.CreatePoster();
+        poster.Script = GetScriptDocument();
+        return poster;
+    }
+
+    private ScriptDocument? GetScriptDocument()
+    {
+        if (_hasCheckedScript)
         {
-            var poster = base.CreatePoster();
-            poster.Script = GetScriptDocument();
-            return poster;
+            return _script;
         }
 
-        private ScriptDocument? GetScriptDocument()
+        var settings = GetSettings();
+        if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
         {
-            if (_hasCheckedScript)
+            var document = new ScriptDocument(settings.Script);
+            if (!document.Compile())
             {
-                return _script;
+                throw new Exception("Script failed to compile!");
             }
-
-            var settings = GetSettings();
-            if (settings.ScriptEnabled && !string.IsNullOrWhiteSpace(settings.Script))
-            {
-                var document = new ScriptDocument(settings.Script);
-                if (!document.Compile())
-                {
-                    throw new Exception("Script failed to compile!");
-                }
-                _script = document;
-            }
-            else
-            {
-                _script = null;
-            }
-
-            _hasCheckedScript = true;
-            return _script;
+            _script = document;
         }
-
-        public override bool BeforePost(IDataModel<TPostable> model)
+        else
         {
-            return Poster.BeforePost(model);
+            _script = null;
         }
 
-        protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
-        {
-            return Poster.Process(model);
-        }
-        public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
-        {
-            Poster.AfterPost(model, result);
-        }
+        _hasCheckedScript = true;
+        return _script;
+    }
+
+    public override bool BeforePost(IDataModel<TPostable> model)
+    {
+        return Poster.BeforePost(model);
+    }
+
+    protected override IPostResult<TPostable> DoProcess(IDataModel<TPostable> model)
+    {
+        return Poster.Process(model);
+    }
+    public override void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result)
+    {
+        Poster.AfterPost(model, result);
     }
 }

+ 5 - 6
InABox.Poster.Timberline/TimberlinePosterSettings.cs

@@ -4,12 +4,11 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace InABox.Poster.Timberline
+namespace InABox.Poster.Timberline;
+
+public abstract class TimberlinePosterSettings<TEntity> : PosterSettings
 {
-    public abstract class TimberlinePosterSettings<TEntity> : PosterSettings
-    {
-        protected abstract string DefaultScript();
+    protected abstract string DefaultScript();
 
-        public override string DefaultScript(Type TPostable) => DefaultScript();
-    }
+    public override string DefaultScript(Type TPostable) => DefaultScript();
 }

+ 29 - 4
inabox.wpf/Grids/PostableSettingsGrid.cs

@@ -1,6 +1,7 @@
 using InABox.Core;
 using InABox.DynamicGrid;
 using InABox.Wpf.Grids;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -23,6 +24,8 @@ namespace InABox.Wpf
             {
                 var editor = (editorForm.FindEditor(name) as LookupEditorControl)!;
                 (editor.EditorDefinition as ComboLookupEditor)!.Buttons![0].SetEnabled(!string.IsNullOrWhiteSpace(value as string));
+                (editor.EditorDefinition as ComboLookupEditor)!.Buttons![1].SetVisible(
+                    CoreUtils.GetEntityOrNull(value as string)?.IsAssignableTo(typeof(IGlobalSettingsPoster)) == true);
             }
             return new();
         }
@@ -36,7 +39,27 @@ namespace InABox.Wpf
             {
                 var settingsButton = new EditorButton(settings, "Settings", 60, ViewSettings, false);
                 settingsButton.SetEnabled(!string.IsNullOrWhiteSpace(settings.PosterType));
-                combo.Buttons = new EditorButton[] { settingsButton };
+
+                var globalSettingsButton = new EditorButton(settings, "Global Settings", 100, ViewGlobalSettings, false);
+                globalSettingsButton.SetVisible(CoreUtils.GetEntityOrNull(settings.PosterType)?.IsAssignableTo(typeof(IGlobalSettingsPoster)) == true);
+
+                combo.Buttons = [settingsButton, globalSettingsButton];
+            }
+        }
+
+        private void ViewGlobalSettings(object editor, object? item)
+        {
+            if (item is not PostableSettings settings) return;
+
+            var posterType = CoreUtils.GetEntityOrNull(settings.PosterType);
+            var globalSettingsType = posterType?.GetInterfaceDefinition(typeof(IGlobalSettingsPoster<>))?.GenericTypeArguments[0];
+            if (globalSettingsType is null) return;
+
+            var globalPosterSettings = PosterUtils.LoadGlobalPosterSettings(globalSettingsType);
+            var grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicItemsListGrid<>), globalSettingsType);
+            if(grid.EditItems(new object[] { globalPosterSettings }))
+            {
+                PosterUtils.SaveGlobalPosterSettings(globalSettingsType, globalPosterSettings);
             }
         }
 
@@ -44,9 +67,11 @@ namespace InABox.Wpf
         {
             if (item is not PostableSettings settings) return;
 
-            var entityType = CoreUtils.GetEntity(settings.PostableType);
-            var posterType = CoreUtils.GetEntity(settings.PosterType!);
-            var settingsType = posterType.GetInterfaceDefinition(typeof(IPoster<,>))!.GenericTypeArguments[1];
+            var entityType = CoreUtils.GetEntityOrNull(settings.PostableType);
+            if (entityType is null) return;
+
+            var posterType = CoreUtils.GetEntityOrNull(settings.PosterType);
+            var settingsType = posterType?.GetInterfaceDefinition(typeof(IPoster<,>))!.GenericTypeArguments[1];
 
             var posterSettings = PosterUtils.LoadPosterSettings(entityType, settingsType);
             var grid = DynamicGridUtils.CreateDynamicGrid(typeof(PosterSettingsGrid<>), settingsType);