diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs index 42b60bb410..3fa8495a72 100644 --- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs +++ b/src/Umbraco.Web/Mvc/ControllerExtensions.cs @@ -69,23 +69,28 @@ namespace Umbraco.Web.Mvc return sw.ToString().Trim(); } } - + /// /// Renders the partial view to string. /// /// The controller context. /// Name of the view. /// The model. + /// true if it is a Partial view, otherwise false for a normal view /// - 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(); diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index 30483cd022..dc65b726b3 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -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 + /// + /// Ensures that all of the correct DataTokens are added to the route values which are all required for rendering front-end umbraco views + /// + /// + /// + /// + 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 + } + /// /// 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. diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Templates/TemplateRenderer.cs similarity index 75% rename from src/Umbraco.Web/Routing/TemplateRenderer.cs rename to src/Umbraco.Web/Templates/TemplateRenderer.cs index 55abfa2924..ce2da96730 100644 --- a/src/Umbraco.Web/Routing/TemplateRenderer.cs +++ b/src/Umbraco.Web/Templates/TemplateRenderer.cs @@ -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 { /// /// This is used purely for the RenderTemplate functionality in Umbraco /// /// - /// 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. /// 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; } /// /// Gets/sets the page id for the template to render /// - public int PageId { get; set; } + public int PageId { get; private set; } /// /// Gets/sets the alt template to render if there is one /// - 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) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9c98dee1ef..5d8846ba5d 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -312,7 +312,7 @@ - + ASPXCodeBehind diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 4be9eba95f..a8a9c69466 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -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 } } + /// + /// Renders the template for the specified pageId and an optional altTemplateId + /// + /// + /// If not specified, will use the template assigned to the node + /// + 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("", pageId, ex); + } + return new HtmlString(sw.ToString()); + } + } #region RenderMacro diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 0d8f413a58..8a78f77168 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -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("", 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("", 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(); + } + } }