Browse Source

Added StandardActions, including the "Send Email" action

Kenric Nugteren 4 months ago
parent
commit
cd3b7a5018

+ 10 - 3
prs.shared/Grids/EventEditor/Action Editors/ActionEditors.cs

@@ -18,15 +18,22 @@ namespace PRS.Shared.Grids.EventEditor;
 
 public interface IEventActionEditor
 {
-    bool Edit(IEventAction trigger, IEventDataModelDefinition dataModelDefinition);
+    bool Edit(IEventAction action, IEventDataModelDefinition dataModelDefinition);
 }
 
+/// <summary>
+///     Indicates that this class is the editor for <typeparamref name="TAction"/>.
+/// </summary>
+/// <remarks>
+///     It is expected that if <typeparamref name="TAction"/> is generic, then this type should have exactly the same
+///     generic arguments.
+/// </remarks>
 public interface IEventActionEditor<TAction> : IEventActionEditor
     where TAction : IEventAction
 {
-    bool Edit(TAction trigger, IEventDataModelDefinition dataModelDefinition);
+    bool Edit(TAction action, IEventDataModelDefinition dataModelDefinition);
 
-    bool IEventActionEditor.Edit(IEventAction trigger, IEventDataModelDefinition dataModelDefinition) => Edit((TAction)trigger, dataModelDefinition);
+    bool IEventActionEditor.Edit(IEventAction action, IEventDataModelDefinition dataModelDefinition) => Edit((TAction)action, dataModelDefinition);
 }
 
 public static class EventActionEditors

+ 72 - 0
prs.shared/Grids/EventEditor/Action Editors/SendEmailActionEditor.cs

@@ -0,0 +1,72 @@
+using InABox.Core;
+using InABox.DynamicGrid;
+using PRS.Shared.Events;
+using PRSStores.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRS.Shared.Grids.EventEditor;
+
+public class SendEmailActionEditItem : BaseObject
+{
+    [EditorSequence(1)]
+    [ExpressionEditor(null)]
+    public string To { get; set; } = "";
+
+    [EditorSequence(2)]
+    [ExpressionEditor(null)]
+    public string CC { get; set; } = "";
+
+    [EditorSequence(3)]
+    [ExpressionEditor(null)]
+    public string BCC { get; set; } = "";
+
+    [EditorSequence(4)]
+    [ExpressionEditor(null)]
+    public string Subject { get; set; } = "";
+
+    [EditorSequence(5)]
+    [ExpressionEditor(null)]
+    public string Message { get; set; } = "";
+}
+
+public class SendEmailActionEditor<TEvent> : IEventActionEditor<SendEmailEventAction<TEvent>>
+    where TEvent : IEvent
+{
+    public bool Edit(SendEmailEventAction<TEvent> action, IEventDataModelDefinition dataModelDefinition)
+    {
+        var item = new SendEmailActionEditItem
+        {
+            To = action.To,
+            CC = action.CC,
+            BCC = action.BCC,
+            Subject = action.Subject,
+            Message = action.Message,
+        };
+        if(DynamicGridUtils.EditObject(item, customiseGrid: grid =>
+        {
+            grid.OnCustomiseEditor += (sender, items, column, editor) =>
+            {
+                if (editor is ExpressionEditor expressionEditor)
+                {
+                    expressionEditor.VariableNames = dataModelDefinition.GetVariables().Select(x => x.Name).ToList();
+                }
+            };
+        }))
+        {
+            action.To = item.To;
+            action.CC = item.CC;
+            action.BCC = item.BCC;
+            action.Subject = item.Subject;
+            action.Message = item.Message;
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+}

+ 14 - 4
prs.shared/Grids/EventEditor/Action Editors/EventActionGrid.cs → prs.shared/Grids/EventEditor/EventActionGrid.cs

@@ -55,13 +55,16 @@ public class EventActionGrid<TEvent, TDataModel> : DynamicItemsListGrid<EventAct
 
     protected override void DoAdd(bool openEditorOnDirectEdit = false)
     {
-        var types = EventUtils.GetEventActionTypes(typeof(TEvent));
-
         var menu = new ContextMenu();
-        foreach(var type in types)
+        foreach(var type in EventUtils.GetEventActionTypes(typeof(TEvent)))
         {
             menu.AddItem(type.GetCaption(), null, type, MenuAdd_Click);
         }
+        foreach(var type in EventUtils.GetStandardEventActionTypes())
+        {
+            menu.AddItem(type.GetCaption(), null, type, MenuAdd_Click);
+        }
+
         menu.IsOpen = true;
     }
 
@@ -82,7 +85,14 @@ public class EventActionGrid<TEvent, TDataModel> : DynamicItemsListGrid<EventAct
     {
         if (type.IsGenericType)
         {
-            type = type.MakeGenericType(typeof(TEvent).GenericTypeArguments);
+            if(!type.BuildGenericType()
+                .AddInterfaceFrom(typeof(IEntityEvent<>), typeof(TEvent))
+                .AddInterface(typeof(IStandardEventAction<>), typeof(TEvent))
+                .TryBuild(out var newType))
+            {
+                return;
+            }
+            type = newType;
         }
         var action = (Activator.CreateInstance(type) as IEventAction<TEvent>)!;
         if(!EventActionEditors.EditAction<TEvent, TDataModel>(action, DataModelDefinition))

+ 10 - 2
prs.shared/Grids/EventEditor/EventEditor.xaml.cs

@@ -7,6 +7,7 @@ using PRS.Shared.Events;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Diagnostics;
 using System.Linq;
 using System.Reflection;
 using System.Runtime.CompilerServices;
@@ -166,9 +167,16 @@ public partial class EventEditor : UserControl, INotifyPropertyChanged
         Type dataModelType;
 
         var eventType = EventUtils.GetEventType(EventType.Value);
-        if(eventType.GetInterfaceDefinition(typeof(IEntityEvent<>)) is Type entityEvInt)
+        if (eventType.IsGenericTypeDefinition)
         {
-            eventType = eventType.MakeGenericType(EntityType!);
+            if(!eventType.BuildGenericType()
+                .AddInterface(typeof(IEntityEvent<>), EntityType!)
+                .TryBuild(out eventType))
+            {
+                Data = null;
+                HasProperties = false;
+                return;
+            }
         }
         dataModelType = eventType.GetInterfaceDefinition(typeof(IEvent<>))!.GenericTypeArguments[0];
 

+ 22 - 35
prs.stores/Events/Event.cs

@@ -1,4 +1,5 @@
-using Comal.Classes;
+using com.sun.jmx.mbeanserver;
+using Comal.Classes;
 using Expressive;
 using InABox.Core;
 using InABox.Database;
@@ -87,53 +88,44 @@ public static class EventUtils
 
     #region Event Types
 
-    private static Dictionary<string, Type>? _eventTypes;
-    private static Dictionary<string, Type>? _triggerTypes;
-    private static Dictionary<string, Type>? _actionTypes;
-
     private static Dictionary<Type, List<Type>>? _eventTriggerTypes;
     private static Dictionary<Type, List<Type>>? _eventActionTypes;
+    private static List<Type>? _standardEventActionTypes;
 
-    [MemberNotNullWhen(true, nameof(_eventTypes), nameof(_triggerTypes), nameof(_actionTypes), nameof(_eventTriggerTypes), nameof(_eventActionTypes))]
+    [MemberNotNullWhen(true, nameof(_eventTriggerTypes), nameof(_eventActionTypes), nameof(_standardEventActionTypes))]
     private static bool _loadedTypes { get; set; }
 
-    [MemberNotNull(nameof(_eventTypes), nameof(_triggerTypes), nameof(_actionTypes), nameof(_eventTriggerTypes), nameof(_eventActionTypes))]
+    [MemberNotNull(nameof(_eventTriggerTypes), nameof(_eventActionTypes), nameof(_standardEventActionTypes))]
     private static void LoadTypes()
     {
         if (_loadedTypes) return;
 
         _loadedTypes = true;
-        _eventTypes = new();
-        _triggerTypes = new();
-        _actionTypes = new();
         _eventTriggerTypes = new();
         _eventActionTypes = new();
+        _standardEventActionTypes = new();
         foreach(var type in CoreUtils.TypeList(x => true))
         {
-            if (type.HasInterface(typeof(IEvent<>)))
+            if (type.GetInterfaceDefinition(typeof(IEventTrigger<,>)) is Type eventTriggerInterface)
             {
                 if (type.GetConstructor([]) is not null)
                 {
-                    _eventTypes[type.Name] = type;
+                    var eventType = eventTriggerInterface.GenericTypeArguments[0];
+                    eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
+                    _eventTriggerTypes.GetValueOrAdd(eventType).Add(type);
                 }
             }
-            else if (type.GetInterfaceDefinition(typeof(IEventTrigger<,>)) is Type eventTriggerInterface)
+            else if(type.GetInterfaceDefinition(typeof(IStandardEventAction<>)) is Type standardEventAction)
             {
                 if (type.GetConstructor([]) is not null)
                 {
-                    _triggerTypes[type.Name] = type;
-
-                    var eventType = eventTriggerInterface.GenericTypeArguments[0];
-                    eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
-                    _eventTriggerTypes.GetValueOrAdd(eventType).Add(type);
+                    _standardEventActionTypes.Add(type);
                 }
             }
             else if (type.GetInterfaceDefinition(typeof(IEventAction<>)) is Type eventActionInterface)
             {
                 if (type.GetConstructor([]) is not null)
                 {
-                    _actionTypes[type.Name] = type;
-
                     var eventType = eventActionInterface.GenericTypeArguments[0];
                     eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
                     _eventActionTypes.GetValueOrAdd(eventType).Add(type);
@@ -155,23 +147,10 @@ public static class EventUtils
         eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
         return _eventActionTypes.GetValueOrDefault(eventType) ?? Enumerable.Empty<Type>();
     }
-
-    public static Type GetEventType(string type)
-    {
-        LoadTypes();
-        return _eventTypes[type]; // Force exception if not a valid type name.
-    }
-
-    public static Type GetTriggerType(string type)
-    {
-        LoadTypes();
-        return _triggerTypes[type]; // Force exception if not a valid type name.
-    }
-
-    public static Type GetActionType(string type)
+    public static IEnumerable<Type> GetStandardEventActionTypes()
     {
         LoadTypes();
-        return _actionTypes[type]; // Force exception if not a valid type name.
+        return _standardEventActionTypes;
     }
 
     #endregion
@@ -814,3 +793,11 @@ public interface IEventAction<TEvent> : IEventAction
     where TEvent : IEvent
 {
 }
+
+/// <summary>
+/// Marks this event action as being applicable to any event type.
+/// </summary>
+public interface IStandardEventAction<TEvent> : IEventAction<TEvent>
+    where TEvent : IEvent
+{
+}

+ 161 - 0
prs.stores/Events/StandardActions.cs

@@ -0,0 +1,161 @@
+using InABox.Core;
+using InABox.Database;
+using PRS.Shared.Events;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+
+namespace PRSStores.Events;
+
+[Caption("Send Email")]
+public partial class SendEmailEventAction<TEvent> : IStandardEventAction<TEvent>
+    where TEvent : IEvent
+{
+    #region Public Properties
+
+    // Each of these properties are expressions.
+
+    public string To { get; set; } = "";
+
+    public string CC { get; set; } = "";
+
+    public string BCC { get; set; } = "";
+
+    public string Subject { get; set; } = "";
+
+    public string Message { get; set; } = "";
+
+    #endregion
+
+    #region Expressions
+
+    private CoreExpression? _toExpression;
+    public CoreExpression ToExpression
+    {
+        get
+        {
+            if(_toExpression is null || _toExpression.ExpressionString != To)
+            {
+                _toExpression = new CoreExpression(To);
+            }
+            return _toExpression;
+        }
+    }
+
+    private CoreExpression? _ccExpression;
+    public CoreExpression CCExpression
+    {
+        get
+        {
+            if(_ccExpression is null || _ccExpression.ExpressionString != CC)
+            {
+                _ccExpression = new CoreExpression(CC);
+            }
+            return _ccExpression;
+        }
+    }
+
+    private CoreExpression? _bccExpression;
+    public CoreExpression BCCExpression
+    {
+        get
+        {
+            if(_bccExpression is null || _bccExpression.ExpressionString != BCC)
+            {
+                _bccExpression = new CoreExpression(BCC);
+            }
+            return _bccExpression;
+        }
+    }
+
+    private CoreExpression? _subjectExpression;
+    public CoreExpression SubjectExpression
+    {
+        get
+        {
+            if(_subjectExpression is null || _subjectExpression.ExpressionString != Subject)
+            {
+                _subjectExpression = new CoreExpression(Subject, returnType: typeof(string));
+            }
+            return _subjectExpression;
+        }
+    }
+
+    private CoreExpression? _messageExpression;
+    public CoreExpression MessageExpression
+    {
+        get
+        {
+            if(_messageExpression is null || _messageExpression.ExpressionString != Message)
+            {
+                _messageExpression = new CoreExpression(Message, returnType: typeof(string));
+            }
+            return _messageExpression;
+        }
+    }
+
+    #endregion
+
+    public IEnumerable<string> ReferencedVariables => ToExpression.ReferencedVariables
+        .Concat(CCExpression.ReferencedVariables)
+        .Concat(BCCExpression.ReferencedVariables)
+        .Concat(SubjectExpression.ReferencedVariables)
+        .Concat(MessageExpression.ReferencedVariables);
+
+    public string Description => "Create Email";
+
+    public object? Execute(IEventDataModel dataModel)
+    {
+        var mailer = DbFactory.Mailer;
+        if(mailer is not null && !DbFactory.EmailAddress.IsNullOrWhiteSpace())
+        {
+            if (mailer.Connect())
+            {
+                var msg = mailer.CreateMessage();
+                msg.From = DbFactory.EmailAddress;
+
+                msg.To = ConvertAddressList(ToExpression.Evaluate(dataModel));
+                msg.CC = ConvertAddressList(CCExpression.Evaluate(dataModel));
+                msg.BCC = ConvertAddressList(BCCExpression.Evaluate(dataModel));
+                msg.Subject = SubjectExpression.Evaluate<string>(dataModel) ?? "";
+                msg.Body = MessageExpression.Evaluate<string>(dataModel) ?? "";
+
+                mailer.SendMessage(msg);
+            }
+        }
+        return null;
+    }
+
+    private IEnumerable<string> ConvertAddressList(object? value)
+    {
+        if(value is IEnumerable<string> list)
+        {
+            return list;
+        }
+        else if(value is IEnumerable enumerable)
+        {
+            var lst = new List<string>();
+            foreach(var obj in enumerable)
+            {
+                var objStr = obj.ToString();
+                if(objStr is not null)
+                {
+                    lst.Add(objStr);
+                }
+            }
+            return lst;
+        }
+        var str = value?.ToString() ?? "";
+
+        var regex = AddressListRegex();
+        return regex.Matches(str).Select(x => x.Value);
+    }
+
+    [GeneratedRegex(@"[^\s,;]+")]
+    private static partial Regex AddressListRegex();
+}
+