From f10b35780cdd1a15a0030786eee69353e9558a47 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Wed, 23 Jan 2019 10:22:29 +0100 Subject: [PATCH 1/4] - Removed PostFilterParams - Handling of PostFilter using Expression trees --- .../src/common/resources/entity.resource.js | 15 +- src/Umbraco.Web/Editors/EntityController.cs | 131 ++++++++++++++---- .../Editors/TemplateQueryController.cs | 2 +- .../Models/TemplateQuery/OperatorFactory.cs | 32 +++++ .../Models/TemplateQuery/QueryCondition.cs | 12 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 6 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index f864696873..753d180880 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -254,22 +254,13 @@ function entityResource($q, $http, umbRequestHelper) { * * @param {string} type Object type name * @param {string} postFilter optional filter expression which will execute a dynamic where clause on the server - * @param {string} postFilterParams optional parameters for the postFilter expression * @returns {Promise} resourcePromise object containing the entity. * */ - getAll: function (type, postFilter, postFilterParams) { - + getAll: function (type, postFilter) { //need to build the query string manually - var query = "type=" + type + "&postFilter=" + (postFilter ? postFilter : ""); - if (postFilter && postFilterParams) { - var counter = 0; - _.each(postFilterParams, function(val, key) { - query += "&postFilterParams[" + counter + "].key=" + key + "&postFilterParams[" + counter + "].value=" + val; - counter++; - }); - } - + var query = "type=" + type + "&postFilter=" + (postFilter ? encodeURIComponent(postFilter) : ""); + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index b25f3f5af1..19f7158c5d 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Net; using System.Web.Http; using AutoMapper; @@ -11,11 +10,10 @@ using Umbraco.Web.Mvc; using System.Linq; using System.Net.Http; using System.Net.Http.Formatting; +using System.Reflection; using Umbraco.Core.Models; -using Constants = Umbraco.Core.Constants; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using System.Web.Http.Controllers; -using Examine; using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -24,12 +22,14 @@ using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Xml; using Umbraco.Web.Models.Mapping; +using Umbraco.Web.Models.TemplateQuery; using Umbraco.Web.Search; using Umbraco.Web.Services; using Umbraco.Web.Trees; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { /// @@ -242,7 +242,7 @@ namespace Umbraco.Web.Editors }; } - + /// /// Gets an entity by a xpath query /// @@ -863,9 +863,15 @@ namespace Umbraco.Web.Editors } } - public IEnumerable GetAll(UmbracoEntityTypes type, string postFilter, [FromUri]IDictionary postFilterParams) + /// + /// + /// + /// The type of entity. + /// Optional filter - Format like: "BoolVariable==true&IntVariable>=6". Invalid filters are ignored. + /// + public IEnumerable GetAll(UmbracoEntityTypes type, string postFilter) { - return GetResultForAll(type, postFilter, postFilterParams); + return GetResultForAll(type, postFilter); } /// @@ -873,29 +879,28 @@ namespace Umbraco.Web.Editors /// /// /// A string where filter that will filter the results dynamically with linq - optional - /// the parameters to fill in the string where filter - optional /// - private IEnumerable GetResultForAll(UmbracoEntityTypes entityType, string postFilter = null, IDictionary postFilterParams = null) + private IEnumerable GetResultForAll(UmbracoEntityTypes entityType, string postFilter = null) { var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { //TODO: Should we order this by something ? var entities = Services.EntityService.GetAll(objectType.Value).WhereNotNull().Select(Mapper.Map); - return ExecutePostFilter(entities, postFilter, postFilterParams); + return ExecutePostFilter(entities, postFilter); } //now we need to convert the unknown ones switch (entityType) { case UmbracoEntityTypes.Template: var templates = Services.FileService.GetTemplates(); - var filteredTemplates = ExecutePostFilter(templates, postFilter, postFilterParams); + var filteredTemplates = ExecutePostFilter(templates, postFilter); return filteredTemplates.Select(Mapper.Map); case UmbracoEntityTypes.Macro: //Get all macros from the macro service var macros = Services.MacroService.GetAll().WhereNotNull().OrderBy(x => x.Name); - var filteredMacros = ExecutePostFilter(macros, postFilter, postFilterParams); + var filteredMacros = ExecutePostFilter(macros, postFilter); return filteredMacros.Select(Mapper.Map); case UmbracoEntityTypes.PropertyType: @@ -906,7 +911,7 @@ namespace Umbraco.Web.Editors .ToArray() .SelectMany(x => x.PropertyTypes) .DistinctBy(composition => composition.Alias); - var filteredPropertyTypes = ExecutePostFilter(propertyTypes, postFilter, postFilterParams); + var filteredPropertyTypes = ExecutePostFilter(propertyTypes, postFilter); return Mapper.Map, IEnumerable>(filteredPropertyTypes); case UmbracoEntityTypes.PropertyGroup: @@ -917,32 +922,32 @@ namespace Umbraco.Web.Editors .ToArray() .SelectMany(x => x.PropertyGroups) .DistinctBy(composition => composition.Name); - var filteredpropertyGroups = ExecutePostFilter(propertyGroups, postFilter, postFilterParams); + var filteredpropertyGroups = ExecutePostFilter(propertyGroups, postFilter); return Mapper.Map, IEnumerable>(filteredpropertyGroups); case UmbracoEntityTypes.User: var users = Services.UserService.GetAll(0, int.MaxValue, out _); - var filteredUsers = ExecutePostFilter(users, postFilter, postFilterParams); + var filteredUsers = ExecutePostFilter(users, postFilter); return Mapper.Map, IEnumerable>(filteredUsers); case UmbracoEntityTypes.Stylesheet: - if (!postFilter.IsNullOrWhiteSpace() || (postFilterParams != null && postFilterParams.Count > 0)) + if (!postFilter.IsNullOrWhiteSpace()) throw new NotSupportedException("Filtering on stylesheets is not currently supported"); return Services.FileService.GetStylesheets().Select(Mapper.Map); - + case UmbracoEntityTypes.Language: - if (!postFilter.IsNullOrWhiteSpace() || (postFilterParams != null && postFilterParams.Count > 0)) + if (!postFilter.IsNullOrWhiteSpace() ) throw new NotSupportedException("Filtering on languages is not currently supported"); return Services.LocalizationService.GetAllLanguages().Select(Mapper.Map); case UmbracoEntityTypes.DictionaryItem: - if (!postFilter.IsNullOrWhiteSpace() || (postFilterParams != null && postFilterParams.Count > 0)) - throw new NotSupportedException("Filtering on languages is not currently supported"); + if (!postFilter.IsNullOrWhiteSpace()) + throw new NotSupportedException("Filtering on dictionary items is not currently supported"); return GetAllDictionaryItems(); @@ -952,20 +957,90 @@ namespace Umbraco.Web.Editors } } - private IEnumerable ExecutePostFilter(IEnumerable entities, string postFilter, IDictionary postFilterParams) + private IEnumerable ExecutePostFilter(IEnumerable entities, string postFilter) { - // if a post filter is assigned then try to execute it - if (postFilter.IsNullOrWhiteSpace() == false) + if (postFilter.IsNullOrWhiteSpace()) return entities; + + var postFilterConditions = postFilter.Split('&'); + + foreach (var postFilterCondition in postFilterConditions) { - // fixme/task/critical - trouble is, we've killed the dynamic Where thing! - throw new NotImplementedException("oops"); - //return postFilterParams == null - // ? entities.AsQueryable().Where(postFilter).ToArray() - // : entities.AsQueryable().Where(postFilter, postFilterParams).ToArray(); + var queryCondition = BuildQueryCondition(postFilterCondition); + + if (queryCondition != null) + { + var whereClauseExpression = queryCondition.BuildCondition("x"); + + entities = entities.Where(whereClauseExpression.Compile()); + } + } return entities; } + private static QueryCondition BuildQueryCondition(string postFilter) + { + var postFilterParts = postFilter.Split(new[] + { + "=", + "==", + "!=", + "<>", + ">", + "<", + ">=", + "<=" + }, 2, StringSplitOptions.RemoveEmptyEntries); + + if (postFilterParts.Length != 2) + { + return null; + } + + var propertyName = postFilterParts[0]; + var constraintValue = postFilterParts[1]; + var stringOperator = postFilter.Substring(propertyName.Length, + postFilter.Length - propertyName.Length - constraintValue.Length); + Operator binaryOperator; + + try + { + binaryOperator = OperatorFactory.FromString(stringOperator); + } + catch (ArgumentException) + { + // unsupported operators are ignored + return null; + } + + var type = typeof(T); + var property = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); + + if (property == null) + { + return null; + } + + var queryCondition = new QueryCondition() + { + Term = new OperatorTerm() + { + Operator = binaryOperator + }, + ConstraintValue = constraintValue, + Property = new PropertyModel() + { + Alias = propertyName, + Name = propertyName, + Type = property.PropertyType.Name + } + }; + + return queryCondition; + } + + + #region Methods to get all dictionary items private IEnumerable GetAllDictionaryItems() @@ -993,7 +1068,7 @@ namespace Umbraco.Web.Editors GetChildItemsForList(childItem, list); } - } + } #endregion } diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index 73ebdf3bc5..2483446ec2 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -118,7 +118,7 @@ namespace Umbraco.Web.Editors foreach (var condition in model.Filters.Where(x => !x.ConstraintValue.IsNullOrWhiteSpace())) { //x is passed in as the parameter alias for the linq where statement clause - var operation = condition.BuildCondition("x", contents, Properties); + var operation = condition.BuildCondition("x"); contents = contents.Where(operation.Compile()); queryExpression.Append(indent); diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs new file mode 100644 index 0000000000..0b595723f1 --- /dev/null +++ b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs @@ -0,0 +1,32 @@ +using System; + +namespace Umbraco.Web.Models.TemplateQuery +{ + public static class OperatorFactory + { + public static Operator FromString(string stringOperator) + { + if (stringOperator == null) throw new ArgumentNullException(nameof(stringOperator)); + + switch (stringOperator) + { + case "=": + case "==": + return Operator.Equals; + case "!=": + case "<>": + return Operator.NotEquals; + case "<": + return Operator.LessThan; + case "<=": + return Operator.LessThanEqualTo; + case ">": + return Operator.GreaterThan; + case ">=": + return Operator.GreaterThanEqualTo; + default: + throw new ArgumentException($"A operator cannot be created from the specified string '{stringOperator}'", nameof(stringOperator)); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index 009b568556..a58badcf2c 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -18,11 +18,10 @@ namespace Umbraco.Web.Models.TemplateQuery private static Lazy StringContainsMethodInfo => new Lazy(() => typeof(string).GetMethod("Contains", new[] {typeof(string)})); - public static Expression> BuildCondition(this QueryCondition condition, - string parameterAlias, IEnumerable contents, IEnumerable properties) + public static Expression> BuildCondition(this QueryCondition condition, string parameterAlias) { object constraintValue; - switch (condition.Property.Type) + switch (condition.Property.Type.ToLowerInvariant()) { case "string": constraintValue = condition.ConstraintValue; @@ -30,12 +29,15 @@ namespace Umbraco.Web.Models.TemplateQuery case "datetime": constraintValue = DateTime.Parse(condition.ConstraintValue); break; + case "boolean": + constraintValue = Boolean.Parse(condition.ConstraintValue); + break; default: constraintValue = Convert.ChangeType(condition.ConstraintValue, typeof(int)); break; } - var parameterExpression = Expression.Parameter(typeof(IPublishedContent), parameterAlias); + var parameterExpression = Expression.Parameter(typeof(T), parameterAlias); var propertyExpression = Expression.Property(parameterExpression, condition.Property.Alias); var valueExpression = Expression.Constant(constraintValue); @@ -73,7 +75,7 @@ namespace Umbraco.Web.Models.TemplateQuery } var predicate = - Expression.Lambda>(bodyExpression.Reduce(), parameterExpression); + Expression.Lambda>(bodyExpression.Reduce(), parameterExpression); return predicate; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 6736f7512b..d2913dd228 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -172,6 +172,7 @@ + From c213408b925dd827777b7feb2f77b32b0f63ef2c Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 30 Jan 2019 20:24:42 +1100 Subject: [PATCH 2/4] Changes OperatorFactory to internal --- .../Models/TemplateQuery/OperatorFactory.cs | 4 +- .../Models/TemplateQuery/QueryCondition.cs | 73 +------------------ 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs index 0b595723f1..0bae72186a 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs @@ -2,7 +2,7 @@ using System; namespace Umbraco.Web.Models.TemplateQuery { - public static class OperatorFactory + internal static class OperatorFactory { public static Operator FromString(string stringOperator) { @@ -29,4 +29,4 @@ namespace Umbraco.Web.Models.TemplateQuery } } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index a58badcf2c..27cf901e49 100644 --- a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; +using System.Collections.Generic; using Umbraco.Core.Models.PublishedContent; namespace Umbraco.Web.Models.TemplateQuery @@ -12,72 +9,4 @@ namespace Umbraco.Web.Models.TemplateQuery public OperatorTerm Term { get; set; } public string ConstraintValue { get; set; } } - - internal static class QueryConditionExtensions - { - private static Lazy StringContainsMethodInfo => - new Lazy(() => typeof(string).GetMethod("Contains", new[] {typeof(string)})); - - public static Expression> BuildCondition(this QueryCondition condition, string parameterAlias) - { - object constraintValue; - switch (condition.Property.Type.ToLowerInvariant()) - { - case "string": - constraintValue = condition.ConstraintValue; - break; - case "datetime": - constraintValue = DateTime.Parse(condition.ConstraintValue); - break; - case "boolean": - constraintValue = Boolean.Parse(condition.ConstraintValue); - break; - default: - constraintValue = Convert.ChangeType(condition.ConstraintValue, typeof(int)); - break; - } - - var parameterExpression = Expression.Parameter(typeof(T), parameterAlias); - var propertyExpression = Expression.Property(parameterExpression, condition.Property.Alias); - - var valueExpression = Expression.Constant(constraintValue); - Expression bodyExpression; - switch (condition.Term.Operator) - { - case Operator.NotEquals: - bodyExpression = Expression.NotEqual(propertyExpression, valueExpression); - break; - case Operator.GreaterThan: - bodyExpression = Expression.GreaterThan(propertyExpression, valueExpression); - break; - case Operator.GreaterThanEqualTo: - bodyExpression = Expression.GreaterThanOrEqual(propertyExpression, valueExpression); - break; - case Operator.LessThan: - bodyExpression = Expression.LessThan(propertyExpression, valueExpression); - break; - case Operator.LessThanEqualTo: - bodyExpression = Expression.LessThanOrEqual(propertyExpression, valueExpression); - break; - case Operator.Contains: - bodyExpression = Expression.Call(propertyExpression, StringContainsMethodInfo.Value, - valueExpression); - break; - case Operator.NotContains: - var tempExpression = Expression.Call(propertyExpression, StringContainsMethodInfo.Value, - valueExpression); - bodyExpression = Expression.Equal(tempExpression, Expression.Constant(false)); - break; - default: - case Operator.Equals: - bodyExpression = Expression.Equal(propertyExpression, valueExpression); - break; - } - - var predicate = - Expression.Lambda>(bodyExpression.Reduce(), parameterExpression); - - return predicate; - } - } } From 7bb0eb109abd38db2759331f08ce137c18e5a916 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 30 Jan 2019 20:31:44 +1100 Subject: [PATCH 3/4] moves class to separate file --- .../TemplateQuery/QueryConditionExtensions.cs | 74 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 2 files changed, 75 insertions(+) create mode 100644 src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs new file mode 100644 index 0000000000..f01d247b1f --- /dev/null +++ b/src/Umbraco.Web/Models/TemplateQuery/QueryConditionExtensions.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Umbraco.Web.Models.TemplateQuery +{ + internal static class QueryConditionExtensions + { + private static Lazy StringContainsMethodInfo => + new Lazy(() => typeof(string).GetMethod("Contains", new[] {typeof(string)})); + + public static Expression> BuildCondition(this QueryCondition condition, string parameterAlias) + { + object constraintValue; + switch (condition.Property.Type.ToLowerInvariant()) + { + case "string": + constraintValue = condition.ConstraintValue; + break; + case "datetime": + constraintValue = DateTime.Parse(condition.ConstraintValue); + break; + case "boolean": + constraintValue = Boolean.Parse(condition.ConstraintValue); + break; + default: + constraintValue = Convert.ChangeType(condition.ConstraintValue, typeof(int)); + break; + } + + var parameterExpression = Expression.Parameter(typeof(T), parameterAlias); + var propertyExpression = Expression.Property(parameterExpression, condition.Property.Alias); + + var valueExpression = Expression.Constant(constraintValue); + Expression bodyExpression; + switch (condition.Term.Operator) + { + case Operator.NotEquals: + bodyExpression = Expression.NotEqual(propertyExpression, valueExpression); + break; + case Operator.GreaterThan: + bodyExpression = Expression.GreaterThan(propertyExpression, valueExpression); + break; + case Operator.GreaterThanEqualTo: + bodyExpression = Expression.GreaterThanOrEqual(propertyExpression, valueExpression); + break; + case Operator.LessThan: + bodyExpression = Expression.LessThan(propertyExpression, valueExpression); + break; + case Operator.LessThanEqualTo: + bodyExpression = Expression.LessThanOrEqual(propertyExpression, valueExpression); + break; + case Operator.Contains: + bodyExpression = Expression.Call(propertyExpression, StringContainsMethodInfo.Value, + valueExpression); + break; + case Operator.NotContains: + var tempExpression = Expression.Call(propertyExpression, StringContainsMethodInfo.Value, + valueExpression); + bodyExpression = Expression.Equal(tempExpression, Expression.Constant(false)); + break; + default: + case Operator.Equals: + bodyExpression = Expression.Equal(propertyExpression, valueExpression); + break; + } + + var predicate = + Expression.Lambda>(bodyExpression.Reduce(), parameterExpression); + + return predicate; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c399f3f3ae..9bce93b0ff 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -182,6 +182,7 @@ + From d9cb605f226fe6a0e9767a78dc828c61b51ab295 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 31 Jan 2019 14:48:43 +1100 Subject: [PATCH 4/4] Fixing getting the macro to render in the rte, incorrect params being set --- .../src/common/services/macro.service.js | 4 ++++ .../src/common/services/tinymce.service.js | 2 +- .../views/common/infiniteeditors/macropicker/macropicker.html | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js index 6c8c0a6ff2..a91f9d51e4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js @@ -130,6 +130,10 @@ function macroService() { var paramDictionary = {}; var macroAlias = macro.alias; + if (!macroAlias) { + throw "The macro object does not contain an alias"; + } + var syntax; _.each(macroParams, function (item) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 896a360814..0ba795626a 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -1346,7 +1346,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s dialogData: dialogData, submit: function (model) { var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine); - self.insertMacroInEditor(args.editor, macroObject, $scope); + self.insertMacroInEditor(args.editor, macroObject); editorService.close(); }, close: function () { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html index 7e54e2ef8e..d6a5fbe230 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/macropicker/macropicker.html @@ -88,7 +88,7 @@ type="button" button-style="success" label-key="general_submit" - action="selectMacro(model)"> + action="selectMacro(model.selectedMacro)">