Переглянути джерело

Merge branch 'kenric' into DatabasePaging

Kenric Nugteren 11 місяців тому
батько
коміт
8006a02040

+ 4 - 0
InABox.Core/BaseObject.cs

@@ -436,6 +436,10 @@ namespace InABox.Core
                 : default;
         }
 
+        /// <summary>
+        ///     Get all database values (i.e., non-calculated, local properties) for a given object <paramref name="sender"/>.
+        ///     If <paramref name="all"/> is <see langword="false"/>, only retrieve values which have changed.
+        /// </summary>
         public static Dictionary<string, object?> GetValues<T>(this T sender, bool all) where T : BaseObject
         {
             var result = new Dictionary<string, object?>();

+ 2 - 1
InABox.Core/DatabaseSchema/StandardProperty.cs

@@ -142,7 +142,8 @@ namespace InABox.Core
                     _calculated = Property.GetCustomAttribute<AggregateAttribute>() != null
                         || Property.GetCustomAttribute<FormulaAttribute>() != null
                         || Property.GetCustomAttribute<ConditionAttribute>() != null
-                        || Property.GetCustomAttribute<ChildEntityAttribute>() != null;
+                        || Property.GetCustomAttribute<ChildEntityAttribute>() != null
+                        || this.GetParent(x => x.IsCalculated) != null;
                 }
                 return _calculated.Value;
             }

+ 10 - 9
InABox.Core/DigitalForms/DataModel/DigitalFormDataModel.cs

@@ -216,17 +216,18 @@ namespace InABox.Core
             Dictionary<string, object?> variables = new Dictionary<string, object?>();
                 
             var formData = DigitalForm.DeserializeFormData(Instance);
-            foreach (var item in formData.Items())
-                variables[$"Data.{item.Key}"] = item.Value;
-                
-            var instancedata = Instance.GetValues(true);
-            foreach (var item in instancedata)
-                variables[$"Form.{item.Key}"] = item.Value;
+            if(formData != null)
+            {
+                foreach (var item in formData.Items())
+                    variables[$"Data.{item.Key}"] = item.Value;
+            }
                 
-            var entitydata = Entity.GetValues(true);
-            foreach (var item in entitydata)
-                variables[$"{typeof(TEntity).EntityName().Split('.').Last()}.{item.Key}"] = item.Value;
+            foreach (var property in DatabaseSchema.Properties(Instance.GetType()).Where(x => !x.Name.StartsWith("Parent.")))
+                variables[$"Form.{property.Name}"] = property.Getter()(Instance);
                 
+            foreach (var property in DatabaseSchema.Properties(Entity.GetType()))
+                variables[$"{typeof(TEntity).Name}.{property.Name}"] = property.Getter()(Entity);
+
             var exp = new CoreExpression(expression);
             return exp.Evaluate(variables)?.ToString();
         }

+ 6 - 0
InABox.Core/DigitalForms/Forms/DigitalForm.cs

@@ -130,6 +130,9 @@ namespace InABox.Core
         /// 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>
@@ -148,6 +151,9 @@ namespace InABox.Core
         /// 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)

+ 43 - 1
InABox.Core/Editors/BaseEditor.cs

@@ -1,4 +1,5 @@
 using System;
+using System.ComponentModel;
 using System.Linq;
 
 namespace InABox.Core
@@ -28,18 +29,42 @@ namespace InABox.Core
         Summary Summary { get; set; }
 
         SecurityAttribute[] Security { get; set; }
+        
+        Type? Information { get; set; }
 
         BaseEditor CloneEditor();
         public object Clone();
     }
 
+    public interface IEditorInformation
+    {
+        string Display(BaseObject[] items);
+    }
+    
+    public abstract class EditorInformation<T> : IEditorInformation where T : class
+    {
+
+        public abstract string GetInfo(T item);
+        
+        public string Display(BaseObject[] items)
+        {
+            var _results = items.OfType<T>().Select(GetInfo).ToArray();
+            return _results.Length == 0
+                ? ""
+                : _results.Length == 1
+                    ? _results[0]
+                    : "(Multiple Values)";
+            
+        }
+    }
+
     public abstract class BaseEditor : Attribute, IEnclosedEntity, ICloneable, IBaseEditor
     {
 
         private BaseObject _linkedParent;
 
         private string _linkedPath;
-
+        
         public void SetLinkedParent(BaseObject parent)
         {
             _linkedParent = parent;
@@ -105,6 +130,22 @@ namespace InABox.Core
         [NullEditor]
         public SecurityAttribute[] Security { get; set; }
 
+        private Type? _information;
+        public Type? Information
+        {
+            get => _information;
+            set
+            {
+                if (value?.GetInterfaces(typeof(IEditorInformation)) != null)
+                    _information = value;
+                else
+                {
+                    if (value != null)
+                        throw new Exception($"{value.EntityName()} does not implement IEditorInformation!");
+                }
+            }
+        }
+
         public BaseEditor CloneEditor()
         {
             var result = DoClone();
@@ -120,6 +161,7 @@ namespace InABox.Core
             result.Summary = Summary;
             result.ToolTip = ToolTip;
             result.Security = Security.Select(x => x.Clone()).ToArray();
+            result.Information = Information;
 
             return result;
         }

+ 14 - 6
InABox.Core/Postable/PosterUtils.cs

@@ -156,7 +156,8 @@ namespace InABox.Core
                 AppDomain.CurrentDomain.GetAssemblies(),
                 x => x.IsClass
                     && !x.IsAbstract
-                    && x.GetTypeInfo().GenericTypeParameters.Length == 1
+                    // Allow type-specific and generic engines
+                    && x.GetTypeInfo().GenericTypeParameters.Length <= 1
                     && x.HasInterface(typeof(IPosterEngine<,,>))
             ).Select(x =>
             {
@@ -197,13 +198,20 @@ namespace InABox.Core
             {
                 return Result.Error(new Exception("No poster for the given settings"));
             }
-            else if(engines.Count == 1)
-            {
-                return Result.Ok(engines[0].Engine.MakeGenericType(T));
-            }
             else
             {
-                return Result.Ok(engines.Single(x => x.Entity == T).Engine.MakeGenericType(T));
+                var engineType = engines.Count == 1 ? engines[0] : engines.Single(x => x.Entity == T);
+
+                if (engineType.Engine.IsGenericType)
+                {
+                    // If the engine is generic, we need to specify for the entity type.
+                    return Result.Ok(engineType.Engine.MakeGenericType(T));
+                }
+                else
+                {
+                    // If the engine has already been specified for a given entity type, we just return the engine type straight up.
+                    return Result.Ok(engineType.Engine);
+                }
             }
         }
 

+ 30 - 21
inabox.database.sqlite/SQLiteProvider.cs

@@ -610,11 +610,12 @@ namespace InABox.Database.SQLite
                 var cascadeFields = new List<string>();
 
                 var props = DatabaseSchema.RootProperties(otherType)
-                    .Where(x => x.IsEntityLink 
+                    .Where(x => x.IsEntityLink
                                 && x.PropertyType.GetInterfaceDefinition(typeof(IEntityLink<>))?.GenericTypeArguments[0] == type
-                                && !x.HasAttribute<ChildEntityAttribute>());
+                                && !x.IsCalculated);
                 foreach(var prop in props)
-                {                    var fieldname = $"{prop.Name}.ID";
+                {
+                    var fieldname = $"{prop.Name}.ID";
                     if(prop.GetAttribute<EntityRelationshipAttribute>() is EntityRelationshipAttribute attr
                         && attr.Action == DeleteAction.Cascade)
                     {
@@ -628,8 +629,14 @@ namespace InABox.Database.SQLite
                 cascadeFields.Sort();
                 setNullFields.Sort();
 
-                cascades.Add(new(otherType, cascadeFields));
-                setNulls.Add(new(otherType, setNullFields));
+                if(cascadeFields.Count > 0)
+                {
+                    cascades.Add(new(otherType, cascadeFields));
+                }
+                if(setNulls.Count > 0)
+                {
+                    setNulls.Add(new(otherType, setNullFields));
+                }
             }
 
             if(cascades.Count > 0)
@@ -2299,24 +2306,24 @@ namespace InABox.Database.SQLite
             if (item.ID == Guid.Empty)
                 item.ID = Guid.NewGuid();
 
-            Dictionary<string, object> insert = item.GetValues(true);
-            Dictionary<string, object> update = item.GetValues(false);
+            var insert = item.GetValues(true);
+            var update = item.GetValues(false);
 
             var insertfields = new List<string>();
             var insertvalues = new List<string>();
             var updatecommands = new List<string>();
 
             var iParam = 0;
-            foreach (var key in insert.Keys)
+            foreach (var (key, v) in insert)
             {
-                if (insert[key] is UserProperties)
+                if (v is UserProperties)
                     continue;
 
                 var sParam = string.Format("@p{0}", iParam++);
                 object? value = null;
                 try
                 {
-                    value = Encode(insert[key], insert[key]?.GetType());
+                    value = Encode(v, v?.GetType());
                 }
                 catch (Exception e)
                 {
@@ -2334,17 +2341,19 @@ namespace InABox.Database.SQLite
                         updatecommands.Add(string.Format("[{0}]={1}", key, sParam));
             }
 
-            var particles = new List<string>();
-            particles.Add("INSERT INTO");
-            particles.Add(T.EntityName().Split('.').Last());
-            particles.Add("(");
-            particles.Add(string.Join(",", insertfields));
-            particles.Add(")");
-            particles.Add("VALUES");
-            particles.Add("(");
-            particles.Add(string.Join(",", insertvalues));
-            particles.Add(")");
-            if (updatecommands.Any())
+            var particles = new List<string>
+            {
+                "INSERT INTO",
+                T.EntityName().Split('.').Last(),
+                "(",
+                string.Join(",", insertfields),
+                ")",
+                "VALUES",
+                "(",
+                string.Join(",", insertvalues),
+                ")"
+            };
+            if (updatecommands.Count != 0)
                 particles.Add("ON CONFLICT([ID]) DO UPDATE SET " + string.Join(", ", updatecommands));
             else
                 particles.Add("ON CONFLICT DO NOTHING");

+ 2 - 1
inabox.wpf/DigitalForms/Designer/Controls/DynamicFormFieldControl.cs

@@ -90,7 +90,8 @@ namespace InABox.DynamicGrid
         protected override void AfterSetControl(TField control)
         {
             base.AfterSetControl(control);
-            if (!string.IsNullOrWhiteSpace(control.Properties.Expression) || control.Properties.ReadOnlyProperty)
+            if (!string.IsNullOrWhiteSpace(control.Properties.Expression)
+                || (!control.Properties.Property.IsNullOrWhiteSpace() && control.Properties.ReadOnlyProperty))
             {
                 IsEnabled = false;
             }

+ 1 - 1
inabox.wpf/DigitalForms/DigitalFormGrid.cs

@@ -395,7 +395,7 @@ namespace InABox.DynamicGrid
                     var appliesTo = items?.Select(x => x.AppliesTo).Distinct().SingleOrDefault();
                     if (!appliesTo.IsNullOrWhiteSpace() && DFUtils.GetFormInstanceType(appliesTo) is Type instanceType)
                     {
-                        foreach(var property in DatabaseSchema.Properties(instanceType).Where(x => !x.Name.StartsWith("Parent")))
+                        foreach(var property in DatabaseSchema.Properties(instanceType).Where(x => !x.Name.StartsWith("Parent.")))
                             variables.Add($"Form.{property.Name}");
                     }
                     

+ 1 - 1
inabox.wpf/DynamicGrid/DynamicEditorGrid.xaml.cs

@@ -285,7 +285,7 @@ public partial class DynamicEditorGrid : UserControl, IDynamicEditorHost
                     GeneralHeight += iHeight + 5.0F;
                 }
 
-                double iWidth = element.DesiredWidth();
+                double iWidth = element.EditorDefinition?.Information != null ? int.MaxValue : element.DesiredWidth();
                 if (iWidth == int.MaxValue)
                 {
                     element.HorizontalAlignment = HorizontalAlignment.Stretch;

+ 46 - 4
inabox.wpf/DynamicGrid/Editors/BaseDynamicEditorControl.cs

@@ -1,3 +1,4 @@
+using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
@@ -5,6 +6,7 @@ using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Media;
 using InABox.Core;
+using RoslynPad.Editor;
 
 namespace InABox.DynamicGrid
 {
@@ -42,6 +44,8 @@ namespace InABox.DynamicGrid
             get => (string)GetValue(ColumnNameProperty);
             set => SetValue(ColumnNameProperty, value);
         }
+        
+        protected TextBox? Information { get; private set; }
 
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
         public IBaseEditor EditorDefinition
@@ -50,11 +54,44 @@ namespace InABox.DynamicGrid
             set
             {
                 _editordefinition = value;
-                Content = CreateEditor();
+                
+                if (_editordefinition.Information != null)
+                {
+                    DockPanel _dock = new DockPanel();
+                    var _editor = CreateEditor();
+                    _editor.SetValue(DockPanel.DockProperty, Dock.Left);
+                    _dock.Children.Add(_editor);
+                    Information = new TextBox()
+                    {
+                        VerticalAlignment = VerticalAlignment.Stretch,
+                        VerticalContentAlignment = VerticalAlignment.Center,
+                        HorizontalAlignment = HorizontalAlignment.Stretch,
+                        Margin = new Thickness(5, 0, 0, 0),
+                        IsReadOnly = true,
+                        Focusable = false,
+                        Background = new SolidColorBrush(Colors.WhiteSmoke),
+                        BorderBrush = new SolidColorBrush(Colors.Silver)
+                    };
+                    Information.SetValue(DockPanel.DockProperty, Dock.Left);
+                    _dock.Children.Add(Information);
+                    Content = _dock;
+                }
+                else
+                    Content = CreateEditor();
+
                 SetColor(Color);
             }
         }
 
+        protected void UpdateInformation()
+        {
+            if (EditorDefinition?.Information != null  && Information != null)
+            {
+                var _info = Activator.CreateInstance(EditorDefinition.Information) as IEditorInformation;
+                Information.Text = _info?.Display(Host.GetItems()) ?? "";
+            }
+        }
+
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
         public bool Loaded { get; set; }
 
@@ -72,8 +109,11 @@ namespace InABox.DynamicGrid
 
         public abstract void SetVisible(bool visible);
 
-        public abstract void SetValue(string property, object? value);
-
+        public virtual void SetValue(string property, object? value)
+        {
+            UpdateInformation();
+        }
+        
         /// <summary>
         /// 
         /// </summary>
@@ -145,8 +185,10 @@ namespace InABox.DynamicGrid
             }
             return buttons;
         }
+        
+        
     }
-
+    
     public abstract class BaseDynamicEditorControl<TEditor> : BaseDynamicEditorControl
         where TEditor : IBaseEditor
     {

+ 7 - 6
inabox.wpf/DynamicGrid/Editors/CodePopupEditor/CodePopupEditorControl.cs

@@ -34,8 +34,8 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
             VerticalAlignment = VerticalAlignment.Stretch,
             HorizontalAlignment = HorizontalAlignment.Stretch
         };
-        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(130, GridUnitType.Pixel) });
-        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(20, GridUnitType.Pixel) });
+        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(131, GridUnitType.Pixel) });
+        Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(19, GridUnitType.Pixel) });
         Grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
 
 
@@ -46,7 +46,7 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
             VerticalContentAlignment = VerticalAlignment.Center,
             HorizontalAlignment = HorizontalAlignment.Stretch
         };
-        Editor.BorderThickness = new Thickness(0.75);
+        Editor.BorderThickness = new Thickness(1,1,0,1);
         Editor.BorderBrush = new SolidColorBrush(Colors.Silver);
         Editor.SetValue(Grid.ColumnProperty, 0);
         Editor.SetValue(Grid.RowProperty, 0);
@@ -65,8 +65,8 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
             Content = "..",
             Focusable = false
         };
-        Select.BorderThickness = new Thickness(0, 0.75, 0.75, 0.75);
-        Select.BorderBrush = new SolidColorBrush(Colors.Gray);
+        Select.BorderThickness = new Thickness(1);
+        Select.BorderBrush = new SolidColorBrush(Colors.DimGray);
         Select.SetValue(Grid.ColumnProperty, 1);
         Select.SetValue(Grid.RowProperty, 0);
         Select.Click += Select_Click;
@@ -207,6 +207,7 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
 
                 foreach (var col in columns)
                     OtherValues[col.Property] = list.OtherValues[col.Property];
+
                 CheckChanged();
 
                 Editor.Text = string.Format("{0}", list.OtherValues[CodeColumn]);
@@ -261,6 +262,7 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
     {
         var client = ClientFactory.CreateClient(_type);
         var columns = LookupFactory.DefineLookupColumns(Host.GetEditorType(), _type, ColumnName);
+        columns.Add(CodeColumn);
 
         var sort = LookupFactory.DefineSort(_type);
         var filter = Filter.Create(_type, column).IsEqualTo(value);
@@ -287,7 +289,6 @@ public class CodePopupEditorControl : DynamicEditorControl<Guid, CodePopupEditor
         _value = id;
         Editor.Text = code;
         
-
         Description.Text = LookupFactory.FormatLookup(_type, display, new[] { CodeColumn });
     }
 

+ 2 - 1
inabox.wpf/DynamicGrid/Editors/DateTimeEditor/DateTimeEditorControl.cs

@@ -38,7 +38,8 @@ namespace InABox.DynamicGrid
                 VerticalContentAlignment = VerticalAlignment.Center,
                 HorizontalContentAlignment = HorizontalAlignment.Center,
                 CalendarDisplayMode = CalendarMode.Month,
-                ShowButtonSpinner = false
+                ShowButtonSpinner = false,
+                Width=150
             };
 
             Editor.GotFocus += (o, e) => { new Timer(s => { Dispatcher.Invoke(() => { Editor.SelectAll(); }); }, Editor, 100, Timeout.Infinite); };

+ 4 - 1
inabox.wpf/DynamicGrid/Editors/DynamicEditorControl.cs

@@ -1,4 +1,5 @@
-using InABox.Core;
+using System;
+using InABox.Core;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Windows;
@@ -74,6 +75,7 @@ namespace InABox.DynamicGrid
                     Changed = true;
                     Updating = false;
                 }
+                UpdateInformation();
             }
 
             return Changed;
@@ -82,6 +84,7 @@ namespace InABox.DynamicGrid
         public override void SetValue(string property, object? value)
         {
             UpdateValue(value != null ? (T)value : default);
+            base.SetValue(property,value);
         }
 
         public override object? GetValue(string property)

+ 1 - 0
inabox.wpf/DynamicGrid/Editors/DynamicEnclosedEditorControl.cs

@@ -92,6 +92,7 @@ namespace InABox.DynamicGrid
             if (!property.StartsWith($"{ColumnName}.")) return;
             property = property[(ColumnName.Length + 1)..];
             SetChildValue(property, value);
+            base.SetValue(property,value);
         }
     }
 }