Fixes #U4-1174 - allows MVC views to render when using RenderTemplate. Added new UmbracoHelper method RenderTemplate. Cleaned up some code and

ensuring that the string writers are disposed. Moved TemplateRenderer to a better namespace.
This commit is contained in:
Shannon Deminick
2012-11-14 10:47:40 +05:00
parent 8597876c92
commit 9554c6a1c6
6 changed files with 110 additions and 50 deletions

View File

@@ -69,23 +69,28 @@ namespace Umbraco.Web.Mvc
return sw.ToString().Trim();
}
}
/// <summary>
/// Renders the partial view to string.
/// </summary>
/// <param name="controller">The controller context.</param>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <param name="isPartial">true if it is a Partial view, otherwise false for a normal view </param>
/// <returns></returns>
internal static string RenderPartialViewToString(this ControllerBase controller, string viewName, object model)
internal static string RenderViewToString(this ControllerBase controller, string viewName, object model, bool isPartial = false)
{
if (controller.ControllerContext == null)
throw new ArgumentException("The controller must have an assigned ControllerContext to execute this method.");
controller.ViewData.Model = model;
using (StringWriter sw = new StringWriter())
using (var sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
var viewResult = !isPartial
? ViewEngines.Engines.FindView(controller.ControllerContext, viewName, null)
: ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();

View File

@@ -65,13 +65,11 @@ namespace Umbraco.Web.Mvc
{
throw new NullReferenceException("There is not current PublishedContentRequest, it must be initialized before the RenderRouteHandler executes");
}
var renderModel = new RenderModel(docRequest.PublishedContent, docRequest.Culture);
//put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine
requestContext.RouteData.DataTokens.Add("umbraco", renderModel); //required for the RenderModelBinder
requestContext.RouteData.DataTokens.Add("umbraco-doc-request", docRequest); //required for RenderMvcController
requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage
SetupRouteDataForRequest(
new RenderModel(docRequest.PublishedContent, docRequest.Culture),
requestContext,
docRequest);
return GetHandlerForRoute(requestContext, docRequest);
@@ -79,6 +77,20 @@ namespace Umbraco.Web.Mvc
#endregion
/// <summary>
/// Ensures that all of the correct DataTokens are added to the route values which are all required for rendering front-end umbraco views
/// </summary>
/// <param name="renderModel"></param>
/// <param name="requestContext"></param>
/// <param name="docRequest"></param>
internal void SetupRouteDataForRequest(RenderModel renderModel, RequestContext requestContext, PublishedContentRequest docRequest)
{
//put essential data into the data tokens, the 'umbraco' key is required to be there for the view engine
requestContext.RouteData.DataTokens.Add("umbraco", renderModel); //required for the RenderModelBinder and view engine
requestContext.RouteData.DataTokens.Add("umbraco-doc-request", docRequest); //required for RenderMvcController
requestContext.RouteData.DataTokens.Add("umbraco-context", UmbracoContext); //required for UmbracoTemplatePage
}
/// <summary>
/// Checks the request and query strings to see if it matches the definition of having a Surface controller
/// posted value, if so, then we return a PostedDataProxyInfo object with the correct information.

View File

@@ -1,23 +1,21 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;
using umbraco;
using System.Linq;
namespace Umbraco.Web.Routing
namespace Umbraco.Web.Templates
{
/// <summary>
/// This is used purely for the RenderTemplate functionality in Umbraco
/// </summary>
/// <remarks>
/// Currently the only place where you can use RenderTemplate is from within the library class (which is legacy)
/// but i guess we still need to support it. In order to do that we need to be able to render both an MVC and Webforms
/// template. So we will do a Server.Execute to execute this handler, this will then need to find put the 'id' request through
/// the routing logic again, the execute the correct handler.
/// Its is pretty round about but is required.
/// This allows you to render either an MVC or Webforms template based purely off of a node id and an optional alttemplate id as string output.
/// </remarks>
internal class TemplateRenderer
{
@@ -27,21 +25,23 @@ namespace Umbraco.Web.Routing
private PublishedContentRequest _oldPublishedContentRequest;
private object _oldAltTemplate;
public TemplateRenderer(UmbracoContext umbracoContext)
public TemplateRenderer(UmbracoContext umbracoContext, int pageId, int? altTemplateId)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
PageId = pageId;
AltTemplate = altTemplateId;
_umbracoContext = umbracoContext;
}
/// <summary>
/// Gets/sets the page id for the template to render
/// </summary>
public int PageId { get; set; }
public int PageId { get; private set; }
/// <summary>
/// Gets/sets the alt template to render if there is one
/// </summary>
public int? AltTemplate { get; set; }
public int? AltTemplate { get; private set; }
public void Render(StringWriter writer)
{
@@ -103,7 +103,7 @@ namespace Umbraco.Web.Routing
RestoreItems();
}
private void ExecuteTemplateRendering(StringWriter sw, PublishedContentRequest contentRequest)
private void ExecuteTemplateRendering(TextWriter sw, PublishedContentRequest contentRequest)
{
//NOTE: Before we used to build up the query strings here but this is not necessary because when we do a
// Server.Execute in the TemplateRenderer, we pass in a 'true' to 'preserveForm' which automatically preserves all current
@@ -112,11 +112,29 @@ namespace Umbraco.Web.Routing
//var queryString = _umbracoContext.HttpContext.Request.QueryString.AllKeys
// .ToDictionary(key => key, key => context.Request.QueryString[key]);
switch (contentRequest.RenderingEngine)
{
case RenderingEngine.Mvc:
throw new NotImplementedException("Currently the TemplateRender does not support rendering MVC templates");
var requestContext = new RequestContext(_umbracoContext.HttpContext, new RouteData()
{
Route = RouteTable.Routes["Umbraco_default"]
});
var routeHandler = new RenderRouteHandler(ControllerBuilder.Current.GetControllerFactory(), _umbracoContext);
var routeDef = routeHandler.GetUmbracoRouteDefinition(requestContext, contentRequest);
var renderModel = new RenderModel(contentRequest.PublishedContent, contentRequest.Culture);
//manually add the action/controller, this is required by mvc
requestContext.RouteData.Values.Add("action", routeDef.ActionName);
requestContext.RouteData.Values.Add("controller", routeDef.ControllerName);
//add the rest of the required route data
routeHandler.SetupRouteDataForRequest(renderModel, requestContext, contentRequest);
//create and assign the controller context
routeDef.Controller.ControllerContext = new ControllerContext(requestContext, routeDef.Controller);
//render as string
var stringOutput = routeDef.Controller.RenderViewToString(
routeDef.ActionName,
renderModel);
sw.Write(stringOutput);
break;
case RenderingEngine.WebForms:
default:
@@ -126,7 +144,8 @@ namespace Umbraco.Web.Routing
// to build up the url again, it will just work.
_umbracoContext.HttpContext.Server.Execute(webFormshandler, sw, true);
break;
}
}
}
private void SetNewItemsOnContextObjects(PublishedContentRequest contentRequest)

View File

@@ -312,7 +312,7 @@
<Compile Include="RouteCollectionExtensions.cs" />
<Compile Include="Routing\LookupByPageIdQuery.cs" />
<Compile Include="Mvc\SurfaceControllerResolver.cs" />
<Compile Include="Routing\TemplateRenderer.cs" />
<Compile Include="Templates\TemplateRenderer.cs" />
<Compile Include="Templates\TemplateUtilities.cs" />
<Compile Include="umbraco.presentation\Default.aspx.cs">
<SubType>ASPXCodeBehind</SubType>

View File

@@ -17,6 +17,7 @@ using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;
using Umbraco.Web.Templates;
using umbraco;
using System.Collections.Generic;
@@ -63,6 +64,28 @@ namespace Umbraco.Web
}
}
/// <summary>
/// Renders the template for the specified pageId and an optional altTemplateId
/// </summary>
/// <param name="pageId"></param>
/// <param name="altTemplateId">If not specified, will use the template assigned to the node</param>
/// <returns></returns>
public IHtmlString RenderTemplate(int pageId, int? altTemplateId = null)
{
var templateRenderer = new TemplateRenderer(_umbracoContext, pageId, altTemplateId);
using (var sw = new StringWriter())
{
try
{
templateRenderer.Render(sw);
}
catch(Exception ex)
{
sw.Write("<!-- Error rendering template with id {0}: '{1}' -->", pageId, ex);
}
return new HtmlString(sw.ToString());
}
}
#region RenderMacro

View File

@@ -12,6 +12,7 @@ using System.Xml.XPath;
using Umbraco.Core;
using Umbraco.Web;
using Umbraco.Web.Routing;
using Umbraco.Web.Templates;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.media;
@@ -1105,25 +1106,21 @@ namespace umbraco
{
if (!UmbracoContext.Current.LiveEditingContext.Enabled)
{
var sw = new StringWriter();
try
{
var altTemplate = TemplateId == -1 ? null : (int?)TemplateId;
var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current)
using (var sw = new StringWriter())
{
try
{
PageId = PageId,
AltTemplate = altTemplate
};
templateRenderer.Render(sw);
}
catch (Exception ee)
{
sw.Write("<!-- Error generating macroContent: '{0}' -->", ee);
}
return sw.ToString();
var altTemplate = TemplateId == -1 ? null : (int?)TemplateId;
var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current, PageId, altTemplate);
templateRenderer.Render(sw);
}
catch (Exception ee)
{
sw.Write("<!-- Error rendering template with id {0}: '{1}' -->", PageId, ee);
}
return sw.ToString();
}
}
else
{
@@ -1136,10 +1133,14 @@ namespace umbraco
var p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode());
p.RenderPage(TemplateId);
var c = p.PageContentControl;
var sw = new StringWriter();
var hw = new HtmlTextWriter(sw);
c.RenderControl(hw);
return sw.ToString();
using (var sw = new StringWriter())
using(var hw = new HtmlTextWriter(sw))
{
c.RenderControl(hw);
return sw.ToString();
}
}
}