diff --git a/src/Umbraco.Core/Dynamics/QueryableExtensions.cs b/src/Umbraco.Core/Dynamics/QueryableExtensions.cs index f90f08cfa2..b7fea7f68b 100644 --- a/src/Umbraco.Core/Dynamics/QueryableExtensions.cs +++ b/src/Umbraco.Core/Dynamics/QueryableExtensions.cs @@ -57,5 +57,7 @@ namespace Umbraco.Core.Dynamics .Invoke(null, new object[] { source, lambda }); return (IOrderedQueryable)result; } + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs b/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs new file mode 100644 index 0000000000..e93b7dc50a --- /dev/null +++ b/src/Umbraco.Tests/Dynamics/QueryableExtensionTests.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using Umbraco.Core.Dynamics; +using Umbraco.Web.Dynamics; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Dynamics +{ + //NOTE: there's libraries in both Umbraco.Core.Dynamics and Umbraco.Web.Dynamics - the reason for this is that the Web.Dynamics + // started with the razor macro implementation and is modified with hard coded references to dynamic node and dynamic null, though it seems + // to still work for other regular classes I don't want to move it to the core without removing these references but that would require a lot of work. + + [TestFixture] + public class QueryableExtensionTests + { + + [Test] + public void Order_By_Test_Int() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().OrderBy("Age").ToArray(); + + Assert.AreEqual(10, result.ElementAt(0).Age); + Assert.AreEqual(11, result.ElementAt(1).Age); + Assert.AreEqual(12, result.ElementAt(2).Age); + Assert.AreEqual(20, result.ElementAt(3).Age); + Assert.AreEqual(31, result.ElementAt(4).Age); + Assert.AreEqual(55, result.ElementAt(5).Age); + + } + + [Test] + public void Order_By_Test_String() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().OrderBy("Name").ToArray(); + + Assert.AreEqual("anothertest", result.ElementAt(0).Name); + Assert.AreEqual("blah", result.ElementAt(1).Name); + Assert.AreEqual("someguy", result.ElementAt(2).Name); + Assert.AreEqual("test1", result.ElementAt(3).Name); + Assert.AreEqual("test2", result.ElementAt(4).Name); + Assert.AreEqual("test3", result.ElementAt(5).Name); + + } + + [Test] + public void Where_Test_String() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Name = \"test1\"").ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_String_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + //NOTE: Currently the object query structure is not supported + //var result = items.AsQueryable().Where("Name = @name", new {name = "test1"}).ToArray(); + var result = items.AsQueryable().Where("Name = @Name", new Dictionary { { "Name", "test1" } }).ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_Int_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Age = @Age", new Dictionary { { "Age", 10 } }).ToArray(); + + Assert.AreEqual(1, result.Count()); + Assert.AreEqual("test1", result.ElementAt(0).Name); + + + } + + [Test] + public void Where_Test_Bool_With_Params() + { + var items = new List + { + new TestModel {Age = 10, Name = "test1", Female = false}, + new TestModel {Age = 31, Name = "someguy", Female = true}, + new TestModel {Age = 11, Name = "test2", Female = true}, + new TestModel {Age = 20, Name = "anothertest", Female = false}, + new TestModel {Age = 55, Name = "blah", Female = false}, + new TestModel {Age = 12, Name = "test3", Female = false} + }; + + var result = items.AsQueryable().Where("Female = @Female", new Dictionary { { "Female", true } }).ToArray(); + + Assert.AreEqual(2, result.Count()); + + + } + + private class TestModel + { + public string Name { get; set; } + public int Age { get; set; } + public bool Female { get; set; } + } + + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 38f3f87c8c..20d9ed5113 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -237,6 +237,7 @@ + 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 164699eb05..ed4a539cba 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 @@ -128,16 +128,29 @@ 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) { + getAll: function (type, postFilter, postFilterParams) { + + //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++; + }); + } + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetAll", - [{type: type }])), + query)), 'Failed to retreive entity data for type ' + type); }, 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 532fe06bb5..6a87ea8a06 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 @@ -216,6 +216,12 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou } + /** when the contents load we need to find any macros declared and load in their content */ + editor.on("LoadContent", function(o) { + var asdf = editor.getContent(); + alert(asdf); + }); + /** This prevents any other commands from executing when the current element is the macro so the content cannot be edited */ editor.on('BeforeExecCommand', function (o) { if (isOnMacroElement) { @@ -310,7 +316,10 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou /** The insert macro button click event handler */ onclick: function () { - var dialogData; + var dialogData = { + //flag for use in rte so we only show macros flagged for the editor + richTextEditor: true + }; //when we click we could have a macro already selected and in that case we'll want to edit the current parameters //so we'll need to extract them and submit them to the dialog. diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js index 437e5a2472..c2d77eb5c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js @@ -104,8 +104,8 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi $scope.wizardStep = "paramSelect"; } - //get the macro list - entityResource.getAll("Macro") + //get the macro list - pass in a filter if it is only for rte + entityResource.getAll("Macro", ($scope.dialogData && $scope.dialogData.richTextEditor && $scope.dialogData.richTextEditor === true) ? "UseInEditor=true" : null) .then(function (data) { $scope.macros = data; diff --git a/src/Umbraco.Web/Dynamics/DynamicQueryable.cs b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs index f4650af6d9..122fa0cd29 100644 --- a/src/Umbraco.Web/Dynamics/DynamicQueryable.cs +++ b/src/Umbraco.Web/Dynamics/DynamicQueryable.cs @@ -11,6 +11,11 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Dynamics { + //TODO: Much of this can move to Umbraco.Core.Dynamics - but somehow need to remove all the hard coded references to things like + // dynamicnull, etc... + //NOTE: The OrderBy stuff here seems to be a bit hacked with hard references to umbraco node objects so don't think it can be + // re-used which is why we have the OrderBy stuff that hasn't been hacked in teh Umbraco.Core.Dynamics + internal static class DynamicQueryable { public static IQueryable Where(this IQueryable source, string predicate, params object[] values) diff --git a/src/Umbraco.Web/Dynamics/ExpressionParser.cs b/src/Umbraco.Web/Dynamics/ExpressionParser.cs index 1ab7b28716..d348682428 100644 --- a/src/Umbraco.Web/Dynamics/ExpressionParser.cs +++ b/src/Umbraco.Web/Dynamics/ExpressionParser.cs @@ -12,6 +12,8 @@ namespace Umbraco.Web.Dynamics { //SD: I wish all of this wasn't hacked and was just the original dynamic linq from MS... sigh. Just // means we can't really use it for anything other than dynamic node (i think) + // I'm fairly sure it's just hte convert to dynamic null stuff... still seems to work for normal linq operations would love to make it + // properly one day. internal class ExpressionParser { diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 05eabdfa86..772f5ecbf3 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -20,6 +20,7 @@ using Constants = Umbraco.Core.Constants; using Examine; using Examine.LuceneEngine.SearchCriteria; using Examine.SearchCriteria; +using Umbraco.Web.Dynamics; namespace Umbraco.Web.Editors { @@ -69,9 +70,9 @@ namespace Umbraco.Web.Editors return GetResultForAncestors(id, type); } - public IEnumerable GetAll(UmbracoEntityTypes type) + public IEnumerable GetAll(UmbracoEntityTypes type, string postFilter, [FromUri]IDictionary postFilterParams) { - return GetResultForAll(type); + return GetResultForAll(type, postFilter, postFilterParams); } private IEnumerable ExamineSearch(string query, bool isContent) @@ -147,20 +148,49 @@ namespace Umbraco.Web.Editors } } - private IEnumerable GetResultForAll(UmbracoEntityTypes entityType) + /// + /// Gets the result for the entity list based on the type + /// + /// + /// 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) { var objectType = ConvertToObjectType(entityType); if (objectType.HasValue) { - return Services.EntityService.GetAll(objectType.Value).Select(Mapper.Map) - .WhereNotNull(); + //TODO: Should we order this by something ? + var entities = Services.EntityService.GetAll(objectType.Value).WhereNotNull().Select(Mapper.Map); + + //if a post filter is assigned then try to execute it + if (postFilter.IsNullOrWhiteSpace() == false) + { + return postFilterParams == null + ? entities.AsQueryable().Where(postFilter).ToArray() + : entities.AsQueryable().Where(postFilter, postFilterParams).ToArray(); + + } + return entities; } //now we need to convert the unknown ones switch (entityType) { case UmbracoEntityTypes.Macro: //Get all macros from the macro service - return Services.MacroService.GetAll().OrderBy(x => x.Name).Select(Mapper.Map); + var result = Services.MacroService.GetAll().WhereNotNull().OrderBy(x => x.Name).AsQueryable(); + + //if a post filter is assigned then try to execute it + if (postFilter.IsNullOrWhiteSpace() == false) + { + result = postFilterParams == null + ? result.Where(postFilter) + : result.Where(postFilter, postFilterParams); + + } + + return result.Select(Mapper.Map); + case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index a3fee00c75..aac9798d23 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -23,6 +23,8 @@ namespace Umbraco.Web.Editors [PluginController("UmbracoApi")] public class MacroController : UmbracoAuthorizedJsonController { + + /// /// Gets the macro parameters to be filled in for a particular macro ///