| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 | using System;using System.Collections.Generic;using System.Linq;using System.Linq.Expressions;using System.Text;using Newtonsoft.Json.Linq;using TextFieldParserStandard;namespace InABox.Core{    // public abstract class DigitalFormsCount : CoreAggregate<DigitalForm, DigitalFormLayout, Guid>    // {    //     public override Expression<Func<DigitalFormLayout, Guid>> Aggregate => x => x.ID;    //    //     public override Filter<DigitalFormLayout> Filter =>     //         new Filter<DigitalFormLayout>(x => x.Type).IsEqualTo(DFLayoutType.Mobile)    //             .And(x=>x.Active).IsEqualTo(true);    //    //     public override Dictionary<Expression<Func<DigitalFormLayout, object>>, Expression<Func<DigitalForm, object>>> Links =>    //         new Dictionary<Expression<Func<DigitalFormLayout, object>>, Expression<Func<DigitalForm, object>>>()    //         {    //             { DigitalFormLayout => DigitalFormLayout.Form.ID, DigitalForm => DigitalForm.ID }    //         };    //    //     public override AggregateCalculation Calculation => AggregateCalculation.Count;    // }    //    // public class ActiveMobileFormsCount : DigitalFormsCount    // {    //     public override Filter<DigitalFormLayout> Filter =>     //         new Filter<DigitalFormLayout>(x => x.Type).IsEqualTo(DFLayoutType.Mobile)    //             .And(x=>x.Active).IsEqualTo(true);    // }    //    // public class ActiveFormsCount : DigitalFormsCount    // {    //     public override Filter<DigitalFormLayout> Filter =>     //         new Filter<DigitalFormLayout>(x=>x.Active).IsEqualTo(true);    // }        [UserTracking("Digital Forms")]    public class DigitalForm : Entity, IRemotable, IPersistent, ILicense<DigitalFormsLicense>//, IDuplicatable    {        /// <summary>        ///     The following functions support PNG, BMP and JPEG        /// </summary>        /// <param name="data"></param>        /// <returns></returns>        private static readonly byte[] bmpHeader = Encoding.ASCII.GetBytes("BM");        private static readonly byte[] pngHeader = { 137, 80, 78, 71 };        private static readonly byte[] jpegHeader = { 255, 216, 255, 224 };        private static readonly byte[] jpeg2Header = { 255, 216, 255, 225 };        [UniqueCodeEditor(Visible = Visible.Default, Editable = Editable.Enabled)]        [EditorSequence(1)]        public string Code { get; set; }        [TextBoxEditor]        [EditorSequence(2)]        public string Description { get; set; }        [ComboLookupEditor(typeof(DigitalFormCategoryLookups))]        [EditorSequence(3)]        public string AppliesTo { get; set; }        [CheckBoxEditor]        [EditorSequence(4)]        public bool Active { get; set; } = true;        [CheckBoxEditor]        [EditorSequence(5)]        public bool Secure { get; set; } = false;        [CheckBoxEditor]        [EditorSequence(6)]        public bool Final { get; set; } = false;        [EditorSequence(7)]        public DigitalFormGroupLink Group => InitializeField(ref _group, nameof(Group));        private DigitalFormGroupLink _group;        [EditorSequence(8)]        [Comment("Expression to calculate 'Description' of form.")]        [ExpressionEditor(null, ToolTip = "Evaluates to a string which becomes the description of the form when saved.")]        public string DescriptionExpression { get; set; } = "";                [EditorSequence(9)]        [Comment("Expression to calculate 'Filename' of form.")]        [ExpressionEditor(null, ToolTip = "Evaluates to a string which becomes the filename of the form when saved.")]        public string ExportExpression { get; set; } = "";        [NullEditor]        public string Report { get; set; }                // [NullEditor]        // [Aggregate(typeof(ActiveFormsCount))]        // public int ActiveForms { get; set; }        //        // [NullEditor]        // [Aggregate(typeof(ActiveMobileFormsCount))]        // public int ActiveMobileForms { get; set; }        public IEntityDuplicator GetDuplicator()        {            var result = new EntityDuplicator<DigitalForm>();            result.AddChild<DigitalForm, DigitalFormVariable, DigitalFormLink>(x => x.Form);            result.AddChild<DigitalForm, DigitalFormLayout, DigitalFormLink>(x => x.Form);            return result;        }        public override string ToString()        {            return string.Format("{0}: {1}", Code, Description);        }        private static bool IsValidImage(byte[] data)        {            return bmpHeader.SequenceEqual(data.Take(bmpHeader.Length))                   || pngHeader.SequenceEqual(data.Take(pngHeader.Length))                   || jpegHeader.SequenceEqual(data.Take(jpegHeader.Length))                   || jpeg2Header.SequenceEqual(data.Take(jpeg2Header.Length));        }        private static bool IsValidImage(string base64Data)        {            var firstBytes = base64Data.Substring(0, Math.Min(8, base64Data.Length));            return IsValidImage(Convert.FromBase64String(firstBytes));        }                /// <summary>        /// Takes a serialized FormData string and BlobData string and deserializes into one dictionary.        /// The result of this is just as if you were deserializing formData as per usual.        /// </summary>        /// <remarks>        ///     Returns <see langword="null"/> if <paramref name="formData"/> is not a valid JSON object.        /// </remarks>        /// <param name="formData"></param>        /// <param name="blobData"></param>        /// <returns></returns>        public static DFLoadStorage? DeserializeFormData(string formData, string? blobData)        {            var values = Serialization.Deserialize<Dictionary<string, object?>>(formData);            if (values is null)                return null;            if (blobData.IsNullOrWhiteSpace())                return new DFLoadStorage(values, null);            var blobs = Serialization.Deserialize<Dictionary<string, object?>>(blobData);            return new DFLoadStorage(values, blobs);        }        /// <summary>        /// Like <see cref="DeserializeFormData(string, string?)"/>, but takes the FormData and BlobData from <paramref name="form"/>,        /// rather than having to specify them manually.        /// </summary>        /// <remarks>        ///     Returns <see langword="null"/> if <c>form.FormData</c> is not a valid JSON object.        /// </remarks>        /// <param name="form"></param>        /// <returns></returns>        public static DFLoadStorage? DeserializeFormData(IDigitalFormInstance form)            => DeserializeFormData(form.FormData, form.BlobData);        /// <summary>        /// Takes a form instance, set of variables and some saved values, and serializes the FormData and BlobData into the respective        /// fields of <paramref name="form"/>.        /// </summary>        /// <remarks>        /// Doesn't return anything, but saves the serialized results directly into form.FormData and form.BlobData.        /// It choose which fields should be saved into BlobData based on whether the form field has the <see cref="IDFBlobField"/> interface.        /// <br/>        /// <paramref name="values"/> should be the same as what you pass into <see cref="Serialization.Serialize(object?, bool)"/>.        /// </remarks>        /// <param name="form">The form instance.</param>        /// <param name="variables">The variables associated with the digital form.</param>        /// <param name="storage">The values to save.</param>        public static void SerializeFormData(IDigitalFormInstance form, ICollection<DigitalFormVariable> variables, DFSaveStorage storage)        {            form.FormData = Serialization.Serialize(storage.FormData);            form.BlobData = Serialization.Serialize(storage.BlobData);        }        private static string? GetVariableData(DigitalFormVariable variable, object value)        {            // TODO: Replace with a function on DFLayoutField or something            var fieldType = variable.FieldType();            if (fieldType == typeof(DFLayoutEmbeddedImage)                || fieldType == typeof(DFLayoutSignaturePad))            {                if (value is byte[]) return IsValidImage((byte[])value) ? Convert.ToBase64String((byte[])value) : null;                var str = value.ToString();                return IsValidImage(str) ? str : null;            }            else if(fieldType == typeof(DFLayoutMultiImage))            {                if (value is JArray || value is IEnumerable<string>) return Serialization.Serialize(value);                return null;            }            return variable.FormatValue(value);        }        /// <summary>        ///     Generates a database FormData from a dictionary of objects. Returns null if the data is invalid (specifically if        ///     any required fields are not present.        /// </summary>        /// <param name="values">.NET objects</param>        /// <param name="variables">The variables of the form needed to be encoded</param>        /// <returns>A string with JSON-encoded FormData, or null if validation requirements are not met.</returns>        public static string? GenerateFormData(Dictionary<string, object> values, IEnumerable<DigitalFormVariable> variables, Entity entity)        {            var data = new Dictionary<string, string>();            foreach (var variable in variables)                if (values.TryGetValue(variable.Code, out var value))                {                    var properties = variable.CreateProperties();                    if (!string.IsNullOrWhiteSpace(properties.Property))                    {                        if (variable.FieldType() == typeof(DFLayoutLookupField))                        {                            if (values.TryGetValue($"{variable.Code}$ID", out var idStr) && Guid.TryParse((string)idStr, out var id))                                CoreUtils.SetPropertyValue(entity, properties.Property, id);                        }                        else                        {                            CoreUtils.SetPropertyValue(entity, properties.Property, value);                        }                    }                    if (value != null)                    {                        var varData = GetVariableData(variable, value);                        if (varData != null)                            data[variable.Code] = varData;                    }                }                else if (variable.Required)                {                    return null;                }            return Serialization.Serialize(data);        }        /// <summary>        ///     Creates a dictionary of objects from a database FormData        /// </summary>        /// <param name="formData">A string with JSON-encoded FormData</param>        /// <param name="variables">The variables of the form needed to be encoded</param>        /// <returns></returns>        public static Dictionary<string, object?> ParseFormData(string formData, IEnumerable<DigitalFormVariable> variables, Entity entity)        {            var data = new Dictionary<string, object?>();            // Could be null            var formObject = new DFLoadStorage(Serialization.Deserialize<Dictionary<string, object?>>(formData) ?? new Dictionary<string, object?>(), null);            foreach (var variable in variables)            {                object? value = null;                var code = variable.Code;                var properties = variable.CreateProperties();                if (!string.IsNullOrWhiteSpace(properties.Property))                {                    value = CoreUtils.GetPropertyValue(entity, properties.Property);                    if (variable.FieldType() == typeof(DFLayoutLookupField))                    {                        code = variable.Code + "$ID";                    }                }                else if (value == null)                {                    value = properties.Deserialize(formObject.GetEntry(code));                }                if (value != null)                {                    data[code] = value;                }            }            return data;        }    }}
 |