| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 | using InABox.Clients;using InABox.Configuration;using InABox.Core.Postable;using System;using System.Collections.Generic;using System.Linq;using System.Reflection;using System.Text;namespace InABox.Core{    public interface IGlobalSettingsPosterEngine    {        IGlobalPosterSettings GetGlobalSettings();        void SaveGlobalSettings(IGlobalPosterSettings settings);    }    /// <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>();        void SaveGlobalSettings(TGlobalSettings settings) => PosterUtils.SaveGlobalPosterSettings(settings);        IGlobalPosterSettings IGlobalSettingsPosterEngine.GetGlobalSettings() => GetGlobalSettings();        void IGlobalSettingsPosterEngine.SaveGlobalSettings(IGlobalPosterSettings settings)        {            if(settings is TGlobalSettings globalSettings)            {                SaveGlobalSettings(globalSettings);            }        }    }    public interface IPosterEngine<TPostable>        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()    {        IPostResult<TPostable>? Process(IDataModel<TPostable> model);    }    public interface IPosterEngine<TPostable, TPoster, TSettings> : IPosterEngine<TPostable>        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()        where TPoster : IPoster<TPostable, TSettings>        where TSettings : PosterSettings, new()    {    }    /// <summary>    /// A base class for all <see cref="IPosterEngine{TPostable}"/>. A concrete instance of this will be loaded by    /// <see cref="PosterUtils.Process{T}(IDataModel{T})"/>; a new instance is guaranteed to be created each time that method is called.    /// </summary>    /// <typeparam name="TPostable"></typeparam>    /// <typeparam name="TPoster"></typeparam>    /// <typeparam name="TSettings"></typeparam>    public abstract class PosterEngine<TPostable, TPoster, TSettings> : IPosterEngine<TPostable, TPoster, TSettings>        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()        where TPoster : class, IPoster<TPostable, TSettings>        where TSettings : PosterSettings, new()    {        protected TPoster Poster;        public PosterEngine()        {            Poster = CreatePoster();        }        private static readonly Type? PosterType = PosterUtils.GetPoster(typeof(TPoster));        protected virtual TPoster CreatePoster()        {            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;        }        private TSettings? _settings;        protected TSettings GetSettings()        {            _settings ??= PosterUtils.LoadPosterSettings<TPostable, TSettings>();            return _settings;        }        protected void SaveSettings(TSettings settings)        {            _settings = settings;            PosterUtils.SavePosterSettings<TPostable, TSettings>(_settings);        }        /// <summary>        /// Returns the <see cref="TSettings.Script"/>, if <see cref="TSettings.ScriptEnabled"/> is <see langword="true"/>;        /// otherwise, returns <see langword="null"/>.        /// </summary>        protected string? GetScript()        {            var settings = GetSettings();            return settings.ScriptEnabled ? settings.Script : null;        }        protected abstract IPostResult<TPostable> DoProcess(IDataModel<TPostable> model);        /// <summary>        /// Process the <paramref name="model"/> before loading;        /// </summary>        /// <param name="model"></param>        /// <returns><see langword="false"/> if the processing must be cancelled.</returns>        public abstract bool BeforePost(IDataModel<TPostable> model);        /// <summary>        /// Prior to saving the <typeparamref name="TPostable"/> entities, make any necessary changes to those entities.        /// This is only called if <see cref="Process(IDataModel{TPostable})"/> returned <see langword="true"/>.        /// </summary>        /// <param name="model"></param>        /// <param name="result">The result object returned by <see cref="DoProcess(IDataModel{TPostable})"/></param>        public abstract void AfterPost(IDataModel<TPostable> model, IPostResult<TPostable> result);        private static void SetFailed(IList<TPostable> entities)        {            foreach (var post in entities)            {                post.PostedStatus = PostedStatus.PostFailed;                post.PostedNote = "Post failed.";            }            new Client<TPostable>().Save(entities, "Post failed by user.");        }        public IPostResult<TPostable>? Process(IDataModel<TPostable> model)        {            if (!BeforePost(model))            {                return null;            }            // Add posted flags; if the columns is null, then we don't need to worry, because it will be loaded.            model.GetColumns<TPostable>()?.Add(x => x.PostedStatus)                .Add(x => x.Posted)                .Add(x => x.PostedNote);            model.LoadModel();            var data = model.GetTable<TPostable>();            if (!data.Rows.Any())            {                throw new EmptyPostException();            }            if(data.Rows.Any(x => x.Get<TPostable, PostedStatus>(x => x.PostedStatus) == PostedStatus.Posted))            {                throw new RepostedException();            }            try            {                var result = DoProcess(model);                AfterPost(model, result);                Client.Save(result.PostedEntities, "Posted by user.");                foreach (var (type, fragments) in result.Fragments)                {                    Client.Create(type).Save(fragments.Cast<Entity>(), "");                }                return result;            }            catch (PostCancelledException)            {                var entities = data.ToObjects<TPostable>().ToList();                SetFailed(entities);                throw;            }            catch (MissingSettingException)            {                throw;            }            catch (PostFailedMessageException)            {                throw;            }            catch(Exception e)            {                Logger.Send(LogType.Error, "", $"Post Failed: {CoreUtils.FormatException(e)}");                var entities = data.ToObjects<TPostable>().ToList();                SetFailed(entities);                throw;            }        }    }    public interface IPullerEngine<TPostable>        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()    {        IPullResult<TPostable>? Pull();    }    public interface IPullerEngine<TPostable, TPoster> : IPullerEngine<TPostable>        where TPostable : Entity, IPostable, IRemotable, IPersistent, new()    {        IPullResult<TPostable> DoPull();        IPullResult<TPostable>? IPullerEngine<TPostable>.Pull()        {            try            {                var result = DoPull();                return result;            }            catch (PullCancelledException)            {                throw;            }            catch (MissingSettingException)            {                throw;            }            catch (PullFailedMessageException)            {                throw;            }            catch(Exception e)            {                Logger.Send(LogType.Error, "", $"Post Failed: {CoreUtils.FormatException(e)}");                throw;            }        }    }}
 |