diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs index 92b439af998..ec85a37b5db 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs +++ b/src/OrchardCore.Modules/OrchardCore.Rules/Drivers/JavascriptConditionDisplayDriver.cs @@ -1,14 +1,39 @@ +using System; using System.Threading.Tasks; +using Esprima; +using Jint.Runtime; +using Microsoft.AspNetCore.Mvc.Localization; +using Microsoft.Extensions.Localization; using OrchardCore.DisplayManagement.Handlers; using OrchardCore.DisplayManagement.ModelBinding; +using OrchardCore.DisplayManagement.Notify; using OrchardCore.DisplayManagement.Views; +using OrchardCore.Mvc.ModelBinding; using OrchardCore.Rules.Models; +using OrchardCore.Rules.Services; using OrchardCore.Rules.ViewModels; namespace OrchardCore.Rules.Drivers { public class JavascriptConditionDisplayDriver : DisplayDriver { + private readonly IHtmlLocalizer H; + private readonly IStringLocalizer S; + private readonly INotifier _notifier; + private readonly JavascriptConditionEvaluator _evaluator; + + public JavascriptConditionDisplayDriver( + IHtmlLocalizer htmlLocalizer, + IStringLocalizer stringLocalizer, + JavascriptConditionEvaluator evaluator, + INotifier notifier) + { + H = htmlLocalizer; + S = stringLocalizer; + _evaluator = evaluator; + _notifier = notifier; + } + public override IDisplayResult Display(JavascriptCondition condition) { return @@ -32,8 +57,40 @@ public override async Task UpdateAsync(JavascriptCondition condi var model = new JavascriptConditionViewModel(); await updater.TryUpdateModelAsync(model, Prefix); - // TODO is empty. - condition.Script = model.Script; + // CodeMirror hides the textarea which displays the error when updater.ModelState.AddModelError() is used, + // that's why a notifier is used to show validation errors. + if (string.IsNullOrWhiteSpace(model.Script)) + { + updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["Please provide a script."]); + await _notifier.ErrorAsync(H["Please provide a script."]); + return Edit(condition); + } + + try + { + _ = await _evaluator.EvaluateAsync(new() + { + ConditionId = condition.ConditionId, + Name = condition.Name, + Script = model.Script + }); + condition.Script = model.Script; + } + catch (ParserException ex) // Invalid syntax + { + updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script couldn't be parsed. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["The script couldn't be parsed. Details: {0}", ex.Message]); + } + catch (JavaScriptException ex) // Evaluation threw an Error + { + updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["JavaScript evaluation resulted in an exception. Details: {0}", ex.Message]); + } + catch (Exception ex) when (ex is InvalidCastException or FormatException) // Evaluation completes successfully, but the result cannot be converted to Boolean + { + updater.ModelState.AddModelError(Prefix, nameof(model.Script), S["The script evaluation failed. Details: {0}", ex.Message]); + await _notifier.ErrorAsync(H["The script evaluation failed. Details: {0}", ex.Message]); + } return Edit(condition); } diff --git a/src/OrchardCore.Modules/OrchardCore.Rules/OrchardCore.Rules.csproj b/src/OrchardCore.Modules/OrchardCore.Rules/OrchardCore.Rules.csproj index 5ec2e46ac53..826283775c7 100644 --- a/src/OrchardCore.Modules/OrchardCore.Rules/OrchardCore.Rules.csproj +++ b/src/OrchardCore.Modules/OrchardCore.Rules/OrchardCore.Rules.csproj @@ -14,6 +14,10 @@ + + + +