using InABox.Clients; using InABox.Configuration; using InABox.Core.Postable; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace InABox.Core { public interface IPosterEngine where TPostable : Entity, IPostable, IRemotable, IPersistent, new() { bool Process(IDataModel model); } public interface IPosterEngine : IPosterEngine where TPostable : Entity, IPostable, IRemotable, IPersistent, new() where TPoster : IPoster where TSettings : PosterSettings, new() { } /// /// A base class for all . A concrete instance of this will be loaded by /// ; a new instance is guaranteed to be created each time that method is called. /// /// /// /// public abstract class PosterEngine : IPosterEngine where TPostable : Entity, IPostable, IRemotable, IPersistent, new() where TPoster : IPoster where TSettings : PosterSettings, new() { protected static TPoster Poster = GetPoster(); private static Type[]? _posters; private static TPoster GetPoster() { _posters ??= CoreUtils.TypeList( AppDomain.CurrentDomain.GetAssemblies(), x => x.IsClass && !x.IsAbstract && !x.IsGenericType && x.HasInterface(typeof(IPoster<,>)) ).ToArray(); var type = _posters.Where(x => typeof(TPoster).IsAssignableFrom(x)).FirstOrDefault() ?? throw new Exception($"No poster of type {typeof(TPoster)}."); return (TPoster)Activator.CreateInstance(type); } protected static TSettings GetSettings() { return PosterUtils.LoadPosterSettings(); } protected static void SaveSettings(TSettings settings) { PosterUtils.SavePosterSettings(settings); } /// /// Returns the , if is ; /// otherwise, returns . /// protected static string? GetScript() { var settings = GetSettings(); return settings.ScriptEnabled ? settings.Script : null; } protected abstract bool DoProcess(IDataModel model); /// /// Process the before loading; /// /// /// if the processing must be cancelled. public abstract bool BeforePost(IDataModel model); /// /// Prior to saving the entities, make any necessary changes to those entities. /// This is only called if returned . /// /// public abstract void AfterPost(IDataModel model); public bool Process(IDataModel model) { if (!BeforePost(model)) { return false; } model.LoadModel(); var data = model.GetTable(); if (!data.Rows.Any()) { throw new EmptyPostException(); } if(data.Rows.Any(x => x.Get(x => x.PostedStatus) == PostedStatus.Posted)) { throw new RepostedException(); } try { var success = DoProcess(model); if (success) { AfterPost(model); } var entities = data.ToObjects().ToList(); if (success) { foreach (var post in entities) { post.Posted = DateTime.Now; post.PostedStatus = PostedStatus.Posted; } new Client().Save(entities, "Posted by user."); } else { foreach (var post in entities) { post.PostedStatus = PostedStatus.PostFailed; } new Client().Save(entities, "Post failed by user."); } return success; } catch(Exception e) { Logger.Send(LogType.Error, "", $"Post Failed: {CoreUtils.FormatException(e)}"); var entities = data.ToObjects().ToList(); foreach (var post in entities) { post.PostedStatus = PostedStatus.PostFailed; } new Client().Save(entities, "Post failed by user."); throw; } } } }