From 9bfeff8c42aaf67e38d53c605ea9cf71af642fd1 Mon Sep 17 00:00:00 2001 From: Gary Sharp Date: Sun, 7 Feb 2021 18:17:03 +1100 Subject: [PATCH] feature: expose custom details to expressions custom details can now be easily retrieved from any expression --- Disco.BI/BI/Interop/Pdf/PdfGenerator.cs | 2 +- Disco.Services/Devices/DeviceExtensions.cs | 2 +- .../Documents/AttachmentImport/Importer.cs | 2 +- .../DocumentTemplateExpressionExtensions.cs | 12 +- .../DocumentTemplatePackageExtensions.cs | 8 +- Disco.Services/Expressions/Expression.cs | 165 +++++++++++------- Disco.Services/Expressions/LazyDictionary.cs | 63 +++++++ Disco.Services/Jobs/JobExtensions.cs | 4 +- .../Users/UserFlags/UserFlagExtensions.cs | 4 +- 9 files changed, 181 insertions(+), 81 deletions(-) create mode 100644 Disco.Services/Expressions/LazyDictionary.cs diff --git a/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs b/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs index 5142e9f3..6a87347d 100644 --- a/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs +++ b/Disco.BI/BI/Interop/Pdf/PdfGenerator.cs @@ -218,7 +218,7 @@ namespace Disco.BI.Interop.Pdf pdfStamper.FormFlattening = FlattenFields; pdfStamper.Writer.CloseStream = false; - IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State); + IDictionary expressionVariables = Expression.StandardVariables(dt, Database, CreatorUser, TimeStamp, State, Data); foreach (string pdfFieldKey in pdfStamper.AcroFields.Fields.Keys) { diff --git a/Disco.Services/Devices/DeviceExtensions.cs b/Disco.Services/Devices/DeviceExtensions.cs index 05006ebe..2f262757 100644 --- a/Disco.Services/Devices/DeviceExtensions.cs +++ b/Disco.Services/Devices/DeviceExtensions.cs @@ -28,7 +28,7 @@ namespace Disco.Services //return Expressions.Expression.TokenizeSingleDynamic(null, deviceProfile.Configuration(context).ComputerNameTemplate, 0); return Expression.TokenizeSingleDynamic(null, deviceProfile.ComputerNameTemplate, 0); }); - var evaluatorVariables = Expression.StandardVariables(null, Database, UserService.CurrentUser, DateTime.Now, null); + var evaluatorVariables = Expression.StandardVariables(null, Database, UserService.CurrentUser, DateTime.Now, null, device); string rendered; try { diff --git a/Disco.Services/Documents/AttachmentImport/Importer.cs b/Disco.Services/Documents/AttachmentImport/Importer.cs index bba9b16b..0c2ad7ad 100644 --- a/Disco.Services/Documents/AttachmentImport/Importer.cs +++ b/Disco.Services/Documents/AttachmentImport/Importer.cs @@ -235,7 +235,7 @@ namespace Disco.Services.Documents.AttachmentImport { try { - var expressionResult = Identifier.DocumentTemplate.EvaluateOnAttachmentImportExpression(attachment, Database, creatorUser, Identifier.TimeStamp, PageIdentifiers); + var expressionResult = Identifier.DocumentTemplate.EvaluateOnAttachmentImportExpression(attachment, Identifier.Target, Database, creatorUser, Identifier.TimeStamp, PageIdentifiers); DocumentsLog.LogImportAttachmentExpressionEvaluated(Identifier.DocumentTemplate, Identifier.Target, attachment, expressionResult); } catch (Exception ex) diff --git a/Disco.Services/Documents/DocumentTemplateExpressionExtensions.cs b/Disco.Services/Documents/DocumentTemplateExpressionExtensions.cs index 9a8d2f0e..d10b8ad4 100644 --- a/Disco.Services/Documents/DocumentTemplateExpressionExtensions.cs +++ b/Disco.Services/Documents/DocumentTemplateExpressionExtensions.cs @@ -22,12 +22,12 @@ namespace Disco.Services ExpressionCache.InvalidateKey("DocumentTemplate_FilterExpression", dt.Id); } - public static bool FilterExpressionMatches(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState State) + public static bool FilterExpressionMatches(this DocumentTemplate dt, IAttachmentTarget Data, DiscoDataContext Database, User User, System.DateTime TimeStamp, DocumentState State) { if (!string.IsNullOrEmpty(dt.FilterExpression)) { var compiledExpression = dt.FilterExpressionFromCache(); - var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State); + var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State, Data); try { var er = compiledExpression.EvaluateFirst(Data, evaluatorVariables); @@ -59,12 +59,12 @@ namespace Disco.Services ExpressionCache.InvalidateKey("DocumentTemplate_OnImportExpression", dt.Id); } - public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, IAttachment Data, DiscoDataContext Database, User User, DateTime TimeStamp, List PageIdentifiers) + public static string EvaluateOnAttachmentImportExpression(this DocumentTemplate dt, IAttachment Data, IAttachmentTarget AttachmentTarget, DiscoDataContext Database, User User, DateTime TimeStamp, List PageIdentifiers) { if (!string.IsNullOrEmpty(dt.OnImportAttachmentExpression)) { var compiledExpression = dt.OnImportAttachmentExpressionFromCache(); - var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null); + var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, null, AttachmentTarget); evaluatorVariables.Add("PageIdentifiers", PageIdentifiers); var result = compiledExpression.EvaluateFirst(Data, evaluatorVariables); if (result == null) @@ -85,12 +85,12 @@ namespace Disco.Services ExpressionCache.InvalidateKey("DocumentTemplate_OnGenerateExpression", dt.Id); } - public static string EvaluateOnGenerateExpression(this DocumentTemplate dt, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) + public static string EvaluateOnGenerateExpression(this DocumentTemplate dt, IAttachmentTarget Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) { if (!string.IsNullOrEmpty(dt.OnGenerateExpression)) { var compiledExpression = dt.OnGenerateExpressionFromCache(); - var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State); + var evaluatorVariables = Expression.StandardVariables(dt, Database, User, TimeStamp, State, Data); var result = compiledExpression.EvaluateFirst(Data, evaluatorVariables); return result.ToString(); diff --git a/Disco.Services/Documents/DocumentTemplatePackageExtensions.cs b/Disco.Services/Documents/DocumentTemplatePackageExtensions.cs index 06b03830..37dd55a1 100644 --- a/Disco.Services/Documents/DocumentTemplatePackageExtensions.cs +++ b/Disco.Services/Documents/DocumentTemplatePackageExtensions.cs @@ -81,12 +81,12 @@ namespace Disco.Services ExpressionCache.InvalidateKey("DocumentTemplatePackage_FilterExpression", package.Id); } - public static bool FilterExpressionMatches(this DocumentTemplatePackage package, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) + public static bool FilterExpressionMatches(this DocumentTemplatePackage package, IAttachmentTarget Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) { if (!string.IsNullOrEmpty(package.FilterExpression)) { var compiledExpression = package.FilterExpressionFromCache(); - var evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State); + var evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State, Data); evaluatorVariables.Add("Package", package); try { @@ -119,12 +119,12 @@ namespace Disco.Services ExpressionCache.InvalidateKey("DocumentTemplatePackage_OnGenerateExpression", package.Id); } - public static string EvaluateOnGenerateExpression(this DocumentTemplatePackage package, object Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) + public static string EvaluateOnGenerateExpression(this DocumentTemplatePackage package, IAttachmentTarget Data, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState State) { if (!string.IsNullOrEmpty(package.OnGenerateExpression)) { Expression compiledExpression = package.OnGenerateExpressionFromCache(); - System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State); + System.Collections.IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, User, TimeStamp, State, Data); evaluatorVariables.Add("Package", package); try { diff --git a/Disco.Services/Expressions/Expression.cs b/Disco.Services/Expressions/Expression.cs index cb4cfcb3..46d5abb4 100644 --- a/Disco.Services/Expressions/Expression.cs +++ b/Disco.Services/Expressions/Expression.cs @@ -2,11 +2,13 @@ using Disco.Data.Repository; using Disco.Models.BI.Expressions; using Disco.Models.Repository; using Disco.Models.Services.Documents; +using Disco.Services.Plugins.Features.DetailsProvider; using Spring.Core.TypeResolution; using Spring.Expressions; using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Disco.Services.Expressions @@ -173,92 +175,127 @@ namespace Disco.Services.Expressions return e; } - public static IDictionary StandardVariables(DocumentTemplate AttachmentType, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState DocumentState) + public static IDictionary StandardVariables(DocumentTemplate AttachmentType, DiscoDataContext Database, User User, DateTime TimeStamp, DocumentState DocumentState, IAttachmentTarget target = null) { - return new Hashtable - { + var detailsVariables = new Dictionary(); + var detailsService = new DetailsProviderService(Database); + if (target != null) + { + if (target is User targetUser) + { + detailsVariables.Add("UserDetails", new LazyDictionary(() => detailsService.GetDetails(targetUser).Details)); + detailsVariables.Add("AssignedDeviceDetails", targetUser.CurrentDeviceUserAssignments().Select(a => new { a.Device, Details = new LazyDictionary(() => detailsService.GetDetails(targetUser).Details) }).ToDictionary(d => d.Device.SerialNumber, d => d.Details, StringComparer.OrdinalIgnoreCase)); + } + else if (target is Job targetJob) + { + detailsVariables.Add("UserDetails", targetJob.User == null ? (IDictionary)new Dictionary() : new LazyDictionary(() => detailsService.GetDetails(targetJob.User).Details)); + detailsVariables.Add("DeviceDetails", targetJob.Device == null ? (IDictionary)new Dictionary() : new LazyDictionary(() => detailsService.GetDetails(targetJob.Device).Details)); + } + else if (target is Device targetDevice) + { + detailsVariables.Add("DeviceDetails", new LazyDictionary(() => detailsService.GetDetails(targetDevice).Details)); + detailsVariables.Add("UserDetails", targetDevice.AssignedUser == null ? (IDictionary)new Dictionary() : new LazyDictionary(() => detailsService.GetDetails(targetDevice.AssignedUser).Details)); + } + } - { - "DataContext", - Database - }, + return new Hashtable(detailsVariables) + { + { + "DataContext", + Database + }, - { - "User", - User - }, + { + "User", + User + }, - { - "TimeStamp", - TimeStamp - }, + { + "TimeStamp", + TimeStamp + }, - { - "AttachmentType", - AttachmentType - }, + { + "AttachmentType", + AttachmentType + }, - { - "State", - DocumentState - } - }; + { + "State", + DocumentState + } + }; } public static Dictionary StandardVariableTypes() { return new Dictionary - { + { - { - "#DataContext", - typeof(DiscoDataContext).AssemblyQualifiedName - }, + { + "#DataContext", + typeof(DiscoDataContext).AssemblyQualifiedName + }, - { - "#User", - typeof(User).AssemblyQualifiedName - }, + { + "#User", + typeof(User).AssemblyQualifiedName + }, - { - "#TimeStamp", - typeof(DateTime).AssemblyQualifiedName - }, + { + "#TimeStamp", + typeof(DateTime).AssemblyQualifiedName + }, - { - "#AttachmentType", - typeof(DocumentTemplate).AssemblyQualifiedName - }, + { + "#AttachmentType", + typeof(DocumentTemplate).AssemblyQualifiedName + }, - { - "#State", - typeof(DocumentState).AssemblyQualifiedName - } - }; + { + "#State", + typeof(DocumentState).AssemblyQualifiedName + }, + + { + "#UserDetails", + typeof(Dictionary).AssemblyQualifiedName + }, + + { + "#DeviceDetails", + typeof(Dictionary).AssemblyQualifiedName + }, + + { + "#AssignedDeviceDetails", + typeof(Dictionary>).AssemblyQualifiedName + }, + }; } public static Dictionary ExtensionLibraryTypes() { return new Dictionary - { - { - "DataExt", - typeof(Extensions.DataExt).AssemblyQualifiedName - }, + { + { + "DataExt", + typeof(Extensions.DataExt).AssemblyQualifiedName + }, - { - "DeviceExt", - typeof(Extensions.DeviceExt).AssemblyQualifiedName - }, - { - "ImageExt", - typeof(Extensions.ImageExt).AssemblyQualifiedName - }, - + "DeviceExt", + typeof(Extensions.DeviceExt).AssemblyQualifiedName + }, + { - "UserExt", - typeof(Extensions.UserExt).AssemblyQualifiedName - } - }; + "ImageExt", + typeof(Extensions.ImageExt).AssemblyQualifiedName + }, + + { + "UserExt", + typeof(Extensions.UserExt).AssemblyQualifiedName + } + }; } } diff --git a/Disco.Services/Expressions/LazyDictionary.cs b/Disco.Services/Expressions/LazyDictionary.cs new file mode 100644 index 00000000..2007df9a --- /dev/null +++ b/Disco.Services/Expressions/LazyDictionary.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Disco.Services.Expressions +{ + public class LazyDictionary : IDictionary + { + private readonly Lazy> dictionary; + + public LazyDictionary(Func> dictionaryFactory) + { + if (dictionaryFactory == null) + throw new ArgumentNullException(nameof(dictionaryFactory)); + + dictionary = new Lazy>(dictionaryFactory); + } + + public string this[string key] + { + get => dictionary.Value.TryGetValue(key, out var value) ? value : null; + set => throw new NotSupportedException(); + } + + public ICollection Keys => dictionary.Value.Keys; + public ICollection Values => dictionary.Value.Values; + public int Count => dictionary.Value.Count; + public bool IsReadOnly => true; + + public void Add(string key, string value) + => throw new NotSupportedException(); + + public void Add(KeyValuePair item) + => throw new NotSupportedException(); + + public void Clear() + => throw new NotSupportedException(); + + public bool Contains(KeyValuePair item) + => dictionary.Value.Contains(item); + + public bool ContainsKey(string key) + => dictionary.Value.ContainsKey(key); + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + => throw new NotSupportedException(); + + public IEnumerator> GetEnumerator() + => dictionary.Value.GetEnumerator(); + + public bool Remove(string key) + => throw new NotSupportedException(); + + public bool Remove(KeyValuePair item) + => throw new NotSupportedException(); + + public bool TryGetValue(string key, out string value) + => dictionary.Value.TryGetValue(key, out value); + + IEnumerator IEnumerable.GetEnumerator() + => dictionary.Value.GetEnumerator(); + } +} diff --git a/Disco.Services/Jobs/JobExtensions.cs b/Disco.Services/Jobs/JobExtensions.cs index 29f6ae75..99a1a3f9 100644 --- a/Disco.Services/Jobs/JobExtensions.cs +++ b/Disco.Services/Jobs/JobExtensions.cs @@ -397,7 +397,7 @@ namespace Disco.Services if (!string.IsNullOrEmpty(Database.DiscoConfiguration.JobPreferences.OnCreateExpression)) { Expression compiledExpression = Jobs.Jobs.OnCreateExpressionFromCache(Database); - IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, job.OpenedTechUser, DateTime.Now, null); + IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, job.OpenedTechUser, DateTime.Now, null, job); object result = compiledExpression.EvaluateFirst(job, evaluatorVariables); if (result == null) return null; @@ -412,7 +412,7 @@ namespace Disco.Services if (!string.IsNullOrEmpty(Database.DiscoConfiguration.JobPreferences.OnCloseExpression)) { Expression compiledExpression = Jobs.Jobs.OnCloseExpressionFromCache(Database); - IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, job.OpenedTechUser, DateTime.Now, null); + IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, job.OpenedTechUser, DateTime.Now, null, job); object result = compiledExpression.EvaluateFirst(job, evaluatorVariables); if (result == null) return null; diff --git a/Disco.Services/Users/UserFlags/UserFlagExtensions.cs b/Disco.Services/Users/UserFlags/UserFlagExtensions.cs index d0dd5beb..ba73e46b 100644 --- a/Disco.Services/Users/UserFlags/UserFlagExtensions.cs +++ b/Disco.Services/Users/UserFlags/UserFlagExtensions.cs @@ -146,7 +146,7 @@ namespace Disco.Services if (!string.IsNullOrEmpty(ufa.UserFlag.OnAssignmentExpression)) { Expression compiledExpression = ufa.UserFlag.OnAssignmentExpressionFromCache(); - IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, AddingUser, TimeStamp, null); + IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, AddingUser, TimeStamp, null, ufa.User); object result = compiledExpression.EvaluateFirst(ufa, evaluatorVariables); if (result == null) return null; @@ -171,7 +171,7 @@ namespace Disco.Services if (!string.IsNullOrEmpty(ufa.UserFlag.OnUnassignmentExpression)) { Expression compiledExpression = ufa.UserFlag.OnUnassignmentExpressionFromCache(); - IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, RemovingUser, TimeStamp, null); + IDictionary evaluatorVariables = Expression.StandardVariables(null, Database, RemovingUser, TimeStamp, null, ufa.User); object result = compiledExpression.EvaluateFirst(ufa, evaluatorVariables); if (result == null) return null;