Fixes: U4-6210 ExtensionMethodFinder doesn't work with overloaded extensions - this means overloaded extension methods for dynamic content won't work. and finalizes: U4-6209 GetGridHtml needs to be an extension on HtmlHelper not IPublishedContent so that ViewData and ModelState are inherited

This commit is contained in:
Shannon
2015-01-30 19:04:39 +11:00
parent 10990e50e6
commit f037f8541c
9 changed files with 362 additions and 302 deletions

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
using Umbraco.Web.Models;
@@ -907,10 +908,12 @@ namespace Umbraco.Web.Dynamics
//SD: I have yet to see extension methods actually being called in the dynamic parsing... need to unit test these
// scenarios and figure out why all this type checking occurs.
var runtimeCache = ApplicationContext.Current != null ? ApplicationContext.Current.ApplicationCache.RuntimeCache : new NullCacheProvider();
if (type == typeof(string) && instanceAsString != null)
{
Expression[] newArgs = (new List<Expression>() { Expression.Invoke(instanceAsString, instanceExpression) }).Concat(args).ToArray();
mb = ExtensionMethodFinder.FindExtensionMethod(typeof(string), newArgs, id, true);
mb = ExtensionMethodFinder.FindExtensionMethod(runtimeCache, typeof(string), newArgs, id, true);
if (mb != null)
{
return CallMethodOnDynamicNode(instance, newArgs, instanceAsString, instanceExpression, (MethodInfo)mb, true);
@@ -919,7 +922,7 @@ namespace Umbraco.Web.Dynamics
if (type == typeof(string) && instanceAsString == null && instance is MemberExpression)
{
Expression[] newArgs = (new List<Expression>() { instance }).Concat(args).ToArray();
mb = ExtensionMethodFinder.FindExtensionMethod(typeof(string), newArgs, id, true);
mb = ExtensionMethodFinder.FindExtensionMethod(runtimeCache, typeof(string), newArgs, id, true);
if (mb != null)
{
return Expression.Call(null, (MethodInfo)mb, newArgs);
@@ -994,7 +997,7 @@ namespace Umbraco.Web.Dynamics
//e.g. uBlogsyPostDate.Date
//SD: Removed the NonPublic accessor here because this will never work in medium trust, wondering why it is NonPublic vs Public ? Have changed to Public.
//MethodInfo ReflectPropertyValue = this.GetType().GetMethod("ReflectPropertyValue", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo ReflectPropertyValue = this.GetType().GetMethod("ReflectPropertyValue", BindingFlags.Public | BindingFlags.Static);
MethodInfo reflectPropertyValue = this.GetType().GetMethod("ReflectPropertyValue", BindingFlags.Public | BindingFlags.Static);
ParameterExpression convertDynamicNullToBooleanFalse = Expression.Parameter(typeof(bool), "convertDynamicNullToBooleanFalse");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
ParameterExpression idParam = Expression.Parameter(typeof(string), "id");
@@ -1008,7 +1011,7 @@ namespace Umbraco.Web.Dynamics
new[] { lambdaResult, result, idParam, convertDynamicNullToBooleanFalse },
Expression.Assign(convertDynamicNullToBooleanFalse, Expression.Constant(_flagConvertDynamicNullToBooleanFalse, typeof(bool))),
Expression.Assign(lambdaResult, Expression.Invoke(instance, lambdaInstanceExpression)),
Expression.Assign(result, Expression.Call(ReflectPropertyValue, lambdaResult, Expression.Constant(id))),
Expression.Assign(result, Expression.Call(reflectPropertyValue, lambdaResult, Expression.Constant(id))),
Expression.IfThen(
Expression.AndAlso(
Expression.TypeEqual(result, typeof(DynamicNull)),

View File

@@ -48,38 +48,30 @@ namespace Umbraco.Web
var model = prop.Value;
var asString = model as string;
if (asString.IsNullOrWhiteSpace()) return new MvcHtmlString(string.Empty);
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
return html.Partial(view, model);
}
[Obsolete("This should not be used, GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, string framework = "bootstrap3")
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, HtmlHelper html, string framework = "bootstrap3")
{
var asString = property.Value as string;
if (asString.IsNullOrWhiteSpace()) return new MvcHtmlString(string.Empty);
var view = "Grid/" + framework;
return new MvcHtmlString(RenderPartialViewToString(view, property.Value));
return html.Partial(view, property.Value);
}
[Obsolete("This should not be used, GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem)
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html)
{
return GetGridHtml(contentItem, "bodyText", "bootstrap3");
return GetGridHtml(contentItem, html, "bodyText", "bootstrap3");
}
[Obsolete("This should not be used, GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias)
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias)
{
Mandate.ParameterNotNullOrEmpty(propertyAlias, "propertyAlias");
return GetGridHtml(contentItem, propertyAlias, "bootstrap3");
return GetGridHtml(contentItem, html, propertyAlias, "bootstrap3");
}
[Obsolete("This should not be used, GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias, string framework)
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework)
{
Mandate.ParameterNotNullOrEmpty(propertyAlias, "propertyAlias");
@@ -89,41 +81,68 @@ namespace Umbraco.Web
var model = prop.Value;
var asString = model as string;
if (asString.IsNullOrWhiteSpace()) return new MvcHtmlString(string.Empty);
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
return new MvcHtmlString(RenderPartialViewToString(view, model));
return html.Partial(view, model);
}
[Obsolete("This should not be used, GetGridHtml extensions on HtmlHelper should be used instead")]
private static string RenderPartialViewToString(string viewName, object model)
//[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, string framework = "bootstrap3")
{
var asString = property.Value as string;
if (asString.IsNullOrWhiteSpace()) return new MvcHtmlString(string.Empty);
using (var sw = new StringWriter())
var htmlHelper = CreateHtmlHelper(property.Value);
return htmlHelper.GetGridHtml(property, framework);
}
//[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem)
{
return GetGridHtml(contentItem, "bodyText", "bootstrap3");
}
//[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias)
{
Mandate.ParameterNotNullOrEmpty(propertyAlias, "propertyAlias");
return GetGridHtml(contentItem, propertyAlias, "bootstrap3");
}
//[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias, string framework)
{
Mandate.ParameterNotNullOrEmpty(propertyAlias, "propertyAlias");
var prop = contentItem.GetProperty(propertyAlias);
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
var model = prop.Value;
var asString = model as string;
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
var htmlHelper = CreateHtmlHelper(model);
return htmlHelper.GetGridHtml(contentItem, propertyAlias, framework);
}
//[Obsolete("This shouldn't need to be used but because the obsolete extension methods above don't have access to the current HtmlHelper, we need to create a fake one, unfortunately however this will not pertain the current views viewdata, tempdata or model state so should not be used")]
private static HtmlHelper CreateHtmlHelper(object model)
{
var cc = new ControllerContext
{
var cc = new ControllerContext
{
RequestContext =
new RequestContext(
UmbracoContext.Current.HttpContext,
new RouteData() { Route = RouteTable.Routes["Umbraco_default"] })
};
RequestContext = UmbracoContext.Current.HttpContext.Request.RequestContext
};
var viewContext = new ViewContext(cc, new FakeView(), new ViewDataDictionary(model), new TempDataDictionary(), new StringWriter());
var htmlHelper = new HtmlHelper(viewContext, new ViewPage());
return htmlHelper;
}
var routeHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory(), UmbracoContext.Current);
var routeDef = routeHandler.GetUmbracoRouteDefinition(cc.RequestContext, UmbracoContext.Current.PublishedContentRequest);
cc.RequestContext.RouteData.Values.Add("action", routeDef.ActionName);
cc.RequestContext.RouteData.Values.Add("controller", routeDef.ControllerName);
var partialView = ViewEngines.Engines.FindPartialView(cc, viewName);
var viewData = new ViewDataDictionary();
var tempData = new TempDataDictionary();
viewData.Model = model;
var viewContext = new ViewContext(cc, partialView.View, viewData, tempData, sw);
partialView.View.Render(viewContext, sw);
partialView.ViewEngine.ReleaseView(cc, partialView.View);
return sw.GetStringBuilder().ToString();
private class FakeView : IView
{
public void Render(ViewContext viewContext, TextWriter writer)
{
}
}
}

View File

@@ -10,6 +10,7 @@ using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Web;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using Umbraco.Core;
@@ -57,7 +58,11 @@ namespace Umbraco.Web.Models
// these two here have leaked in v6 and so we cannot remove them anymore
// without breaking compatibility but... TODO: remove them in v7
[Obsolete("Will be removing in future versions")]
public DynamicPublishedContentList ChildrenAsList { get { return Children; } }
[Obsolete("Will be removing in future versions")]
public int parentId { get { return PublishedContent.Parent.Id; } }
#region DynamicObject
@@ -73,7 +78,9 @@ namespace Umbraco.Web.Models
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[]
var runtimeCache = ApplicationContext.Current != null ? ApplicationContext.Current.ApplicationCache.RuntimeCache : new NullCacheProvider();
var attempt = DynamicInstanceHelper.TryInvokeMember(runtimeCache, this, binder, args, new[]
{
typeof(DynamicPublishedContent)
});

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Dynamic;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
using System.Collections;
using System.Reflection;
@@ -284,8 +285,10 @@ namespace Umbraco.Web.Models
return true;
}
var runtimeCache = ApplicationContext.Current != null ? ApplicationContext.Current.ApplicationCache.RuntimeCache : new NullCacheProvider();
//ok, now lets try to match by member, property, extensino method
var attempt = DynamicInstanceHelper.TryInvokeMember(this, binder, args, new[]
var attempt = DynamicInstanceHelper.TryInvokeMember(runtimeCache, this, binder, args, new[]
{
typeof (IEnumerable<DynamicPublishedContent>),
typeof (DynamicPublishedContentList)