using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using InABox.Core.Reports; using Newtonsoft.Json.Linq; using TextFieldParserStandard; namespace InABox.Core { // public abstract class DigitalFormsCount : CoreAggregate // { // public override Expression> Aggregate => x => x.ID; // // public override Filter Filter => // new Filter(x => x.Type).IsEqualTo(DFLayoutType.Mobile) // .And(x=>x.Active).IsEqualTo(true); // // public override Dictionary>, Expression>> Links => // new Dictionary>, Expression>>() // { // { DigitalFormLayout => DigitalFormLayout.Form.ID, DigitalForm => DigitalForm.ID } // }; // // public override AggregateCalculation Calculation => AggregateCalculation.Count; // } // // public class ActiveMobileFormsCount : DigitalFormsCount // { // public override Filter Filter => // new Filter(x => x.Type).IsEqualTo(DFLayoutType.Mobile) // .And(x=>x.Active).IsEqualTo(true); // } // // public class ActiveFormsCount : DigitalFormsCount // { // public override Filter Filter => // new Filter(x=>x.Active).IsEqualTo(true); // } [UserTracking("Digital Forms")] public class DigitalForm : Entity, IRemotable, IPersistent, ILicense//, IDuplicatable { /// /// The following functions support PNG, BMP and JPEG /// /// /// 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 { get; set; } [EditorSequence(8)] [ExpressionEditor(null, ToolTip = "Evaluates to string which becomes the description of the form when saving.")] public string DescriptionExpression { 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(); result.AddChild(x => x.Form); result.AddChild(x => x.Form); result.AddChild(x => x.EntityLink); result.AddChild(new Column(x => x.Section), x => x.ID.ToString()); 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)); } /// /// 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. /// /// /// /// public static DFLoadStorage? DeserializeFormData(string formData, string? blobData) { var values = Serialization.Deserialize>(formData); if (values is null) return null; if (blobData.IsNullOrWhiteSpace()) return new DFLoadStorage(values, null); var blobs = Serialization.Deserialize>(blobData); return new DFLoadStorage(values, blobs); } /// /// Like , but takes the FormData and BlobData from , /// rather than having to specify them manually. /// /// /// public static DFLoadStorage? DeserializeFormData(IDigitalFormInstance form) => DeserializeFormData(form.FormData, form.BlobData); /// /// Takes a form instance, set of variables and some saved values, and serializes the FormData and BlobData into the respective /// fields of . /// /// /// 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 interface. ///
/// should be the same as what you pass into . ///
/// The form instance. /// The variables associated with the digital form. /// The values to save. public static void SerializeFormData(IDigitalFormInstance form, ICollection 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) return Serialization.Serialize(value); return null; } return variable.FormatValue(value); } /// /// Generates a database FormData from a dictionary of objects. Returns null if the data is invalid (specifically if /// any required fields are not present. /// /// .NET objects /// The variables of the form needed to be encoded /// A string with JSON-encoded FormData, or null if validation requirements are not met. public static string? GenerateFormData(Dictionary values, IEnumerable variables, Entity entity) { var data = new Dictionary(); 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); } /// /// Creates a dictionary of objects from a database FormData /// /// A string with JSON-encoded FormData /// The variables of the form needed to be encoded /// public static Dictionary ParseFormData(string formData, IEnumerable variables, Entity entity) { var data = new Dictionary(); // Could be null var formObject = new DFLoadStorage(Serialization.Deserialize>(formData) ?? new Dictionary(), 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; } } }