Adds dynamic filtering to EntityService (post filtering for GetAll) - updated the insert macro dialog to ensure that it only shows macros flagged for the rte when in rte mode, otherwise shows all of them. adds unit tests for dynamic queryable stuff.

This commit is contained in:
Shannon
2013-09-25 13:54:25 +10:00
parent f127d31928
commit 6c9fe2ebd0
10 changed files with 237 additions and 11 deletions

View File

@@ -57,5 +57,7 @@ namespace Umbraco.Core.Dynamics
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
}

View File

@@ -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<TestModel>
{
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<TestModel>
{
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<TestModel>
{
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<TestModel>
{
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<string, object> { { "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<TestModel>
{
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<string, object> { { "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<TestModel>
{
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<string, object> { { "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; }
}
}
}

View File

@@ -237,6 +237,7 @@
<Compile Include="Controllers\WebApiEditors\FilterAllowedOutgoingContentAttributeTests.cs" />
<Compile Include="Controllers\WebApiEditors\MediaControllerUnitTests.cs" />
<Compile Include="CoreXml\FrameworkXmlTests.cs" />
<Compile Include="Dynamics\QueryableExtensionTests.cs" />
<Compile Include="Integration\CreateContent.cs" />
<Compile Include="Macros\MacroParserTests.cs" />
<Compile Include="Migrations\Upgrades\ValidateV7UpgradeTest.cs" />

View File

@@ -128,16 +128,29 @@ function entityResource($q, $http, umbRequestHelper) {
* </pre>
*
* @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);
},

View File

@@ -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.

View File

@@ -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;

View File

@@ -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<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)

View File

@@ -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<T>
{

View File

@@ -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<EntityBasic> GetAll(UmbracoEntityTypes type)
public IEnumerable<EntityBasic> GetAll(UmbracoEntityTypes type, string postFilter, [FromUri]IDictionary<string, object> postFilterParams)
{
return GetResultForAll(type);
return GetResultForAll(type, postFilter, postFilterParams);
}
private IEnumerable<EntityBasic> ExamineSearch(string query, bool isContent)
@@ -147,20 +148,49 @@ namespace Umbraco.Web.Editors
}
}
private IEnumerable<EntityBasic> GetResultForAll(UmbracoEntityTypes entityType)
/// <summary>
/// Gets the result for the entity list based on the type
/// </summary>
/// <param name="entityType"></param>
/// <param name="postFilter">A string where filter that will filter the results dynamically with linq - optional</param>
/// <param name="postFilterParams">the parameters to fill in the string where filter - optional</param>
/// <returns></returns>
private IEnumerable<EntityBasic> GetResultForAll(UmbracoEntityTypes entityType, string postFilter = null, IDictionary<string, object> postFilterParams = null)
{
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
{
return Services.EntityService.GetAll(objectType.Value).Select(Mapper.Map<EntityBasic>)
.WhereNotNull();
//TODO: Should we order this by something ?
var entities = Services.EntityService.GetAll(objectType.Value).WhereNotNull().Select(Mapper.Map<EntityBasic>);
//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<EntityBasic>);
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<EntityBasic>);
case UmbracoEntityTypes.Domain:
case UmbracoEntityTypes.Language:

View File

@@ -23,6 +23,8 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class MacroController : UmbracoAuthorizedJsonController
{
/// <summary>
/// Gets the macro parameters to be filled in for a particular macro
/// </summary>