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.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)"> diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 5cf248d2a6..ca747e83f5 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 41f38679a9..514d2913ed 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"); //for review - this uses a tonized query rather then the normal linq query. contents = contents.Where(operation.Compile()); diff --git a/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs new file mode 100644 index 0000000000..0bae72186a --- /dev/null +++ b/src/Umbraco.Web/Models/TemplateQuery/OperatorFactory.cs @@ -0,0 +1,32 @@ +using System; + +namespace Umbraco.Web.Models.TemplateQuery +{ + internal 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)); + } + } + } +} diff --git a/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs b/src/Umbraco.Web/Models/TemplateQuery/QueryCondition.cs index 009b568556..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,70 +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, IEnumerable contents, IEnumerable properties) - { - object constraintValue; - switch (condition.Property.Type) - { - case "string": - constraintValue = condition.ConstraintValue; - break; - case "datetime": - constraintValue = DateTime.Parse(condition.ConstraintValue); - break; - default: - constraintValue = Convert.ChangeType(condition.ConstraintValue, typeof(int)); - break; - } - - var parameterExpression = Expression.Parameter(typeof(IPublishedContent), 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; - } - } } 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 6b7f568566..f078e9d79f 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -182,10 +182,12 @@ + +