diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs index 140f0d1de5..c8157941b7 100644 --- a/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs +++ b/src/Umbraco.Web/Routing/PublishedContentRequestBuilder.cs @@ -23,6 +23,7 @@ namespace Umbraco.Web.Routing public PublishedContentRequestBuilder(PublishedContentRequest publishedContentRequest) { + if (publishedContentRequest == null) throw new ArgumentNullException("publishedContentRequest"); _publishedContentRequest = publishedContentRequest; _umbracoContext = publishedContentRequest.RoutingContext.UmbracoContext; _routingContext = publishedContentRequest.RoutingContext; diff --git a/src/Umbraco.Web/Routing/TemplateRenderer.cs b/src/Umbraco.Web/Routing/TemplateRenderer.cs new file mode 100644 index 0000000000..0d0b4ded0f --- /dev/null +++ b/src/Umbraco.Web/Routing/TemplateRenderer.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Web; +using System.Web.Compilation; +using Umbraco.Core; +using umbraco; +using System.Linq; + +namespace Umbraco.Web.Routing +{ + /// + /// 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. + /// + internal class TemplateRenderer + { + private readonly UmbracoContext _umbracoContext; + private object _oldPageId; + private object _oldPageElements; + private PublishedContentRequest _oldPublishedContentRequest; + private object _oldAltTemplate; + + public TemplateRenderer(UmbracoContext umbracoContext) + { + _umbracoContext = umbracoContext; + } + + /// + /// Gets/sets the page id for the template to render + /// + public int PageId { get; set; } + + /// + /// Gets/sets the alt template to render if there is one + /// + public int? AltTemplate { get; set; } + + public IDictionary QueryStrings { get; set; } + + public void Render(StringWriter writer) + { + // instanciate a request a process + // important to use CleanedUmbracoUrl - lowercase path-only version of the current url, though this isn't going to matter + // terribly much for this implementation since we are just creating a doc content request to modify it's properties manually. + var contentRequest = new PublishedContentRequest(_umbracoContext.CleanedUmbracoUrl, _umbracoContext.RoutingContext); + + var doc = contentRequest.RoutingContext.PublishedContentStore.GetDocumentById( + contentRequest.RoutingContext.UmbracoContext, + PageId); + + if (doc == null) + { + writer.Write("", PageId); + return; + } + + //set the culture to the same as is currently rendering + contentRequest.Culture = _umbracoContext.PublishedContentRequest.Culture; + //set the doc that was found by id + contentRequest.PublishedContent = doc; + //set the template, either based on the AltTemplate found or the standard template of the doc + contentRequest.Template = !AltTemplate.HasValue + ? global::umbraco.cms.businesslogic.template.Template.GetTemplate(doc.TemplateId) + : global::umbraco.cms.businesslogic.template.Template.GetTemplate(AltTemplate.Value); + + //if there is not template then exit + if (!contentRequest.HasTemplate) + { + if (!AltTemplate.HasValue) + { + writer.Write("", doc.TemplateId); + } + else + { + writer.Write("", AltTemplate); + } + return; + } + + //ok, we have a document and a template assigned, now to do some rendering. + var builder = new PublishedContentRequestBuilder(contentRequest); + //determine the rendering engine + builder.DetermineRenderingEngine(); + + //First, save all of the items locally that we know are used in the chain of execution, we'll need to restore these + //after this page has rendered. + SaveExistingItems(); + + //set the new items on context objects for this templates execution + SetNewItemsOnContextObjects(contentRequest); + + //Render the template + ExecuteTemplateRendering(writer, contentRequest); + + //restore items on context objects to continuing rendering the parent template + RestoreItems(); + } + + private void ExecuteTemplateRendering(StringWriter sw, PublishedContentRequest contentRequest) + { + switch (contentRequest.RenderingEngine) + { + case RenderingEngine.Mvc: + throw new NotImplementedException("Currently the TemplateRender does not support rendering MVC templates"); + break; + case RenderingEngine.WebForms: + default: + //var webFormshandler = (global::umbraco.UmbracoDefault)BuildManager + // .CreateInstanceFromVirtualPath("~/default.aspx", typeof(global::umbraco.UmbracoDefault)); + var url = ("~/default.aspx?" + QueryStrings.Select(x => x.Key + "=" + x.Value + "&")).TrimEnd("&"); + _umbracoContext.HttpContext.Server.Execute(url, sw, true); + break; + } + } + + private void SetNewItemsOnContextObjects(PublishedContentRequest contentRequest) + { + // handlers like default.aspx will want it and most macros currently need it + contentRequest.UmbracoPage = new page(contentRequest); + //now, set the new ones for this page execution + _umbracoContext.HttpContext.Items["pageID"] = contentRequest.DocumentId; + _umbracoContext.HttpContext.Items["pageElements"] = contentRequest.UmbracoPage.Elements; + _umbracoContext.HttpContext.Items["altTemplate"] = null; + _umbracoContext.PublishedContentRequest = contentRequest; + } + + /// + /// Save all items that we know are used for rendering execution to variables so we can restore after rendering + /// + private void SaveExistingItems() + { + //Many objects require that these legacy items are in the http context items... before we render this template we need to first + //save the values in them so that we can re-set them after we render so the rest of the execution works as per normal. + _oldPageId = _umbracoContext.HttpContext.Items["pageID"]; + _oldPageElements = _umbracoContext.HttpContext.Items["pageElements"]; + _oldPublishedContentRequest = _umbracoContext.PublishedContentRequest; + _oldAltTemplate = _umbracoContext.HttpContext.Items["altTemplate"]; + } + + /// + /// Restores all items back to their context's to continue normal page rendering execution + /// + private void RestoreItems() + { + _umbracoContext.PublishedContentRequest = _oldPublishedContentRequest; + _umbracoContext.HttpContext.Items["pageID"] = _oldPageId; + _umbracoContext.HttpContext.Items["pageElements"] = _oldPageElements; + _umbracoContext.HttpContext.Items["altTemplate"] = _oldAltTemplate; + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ef9c4fda47..9c98dee1ef 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -312,6 +312,7 @@ + ASPXCodeBehind diff --git a/src/Umbraco.Web/umbraco.presentation/library.cs b/src/Umbraco.Web/umbraco.presentation/library.cs index 701e689ff1..62626104f9 100644 --- a/src/Umbraco.Web/umbraco.presentation/library.cs +++ b/src/Umbraco.Web/umbraco.presentation/library.cs @@ -11,6 +11,7 @@ using System.Xml; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Web; +using Umbraco.Web.Routing; using umbraco.BusinessLogic; using umbraco.cms.businesslogic; using umbraco.cms.businesslogic.media; @@ -30,6 +31,7 @@ using System.Collections.Generic; using umbraco.cms.businesslogic.cache; using umbraco.NodeFactory; using UmbracoContext = umbraco.presentation.UmbracoContext; +using System.Linq; namespace umbraco { @@ -1101,36 +1103,29 @@ namespace umbraco { if (UmbracoSettings.UseAspNetMasterPages) { - if (!umbraco.presentation.UmbracoContext.Current.LiveEditingContext.Enabled) + if (!UmbracoContext.Current.LiveEditingContext.Enabled) { - System.Collections.Generic.Dictionary items = getCurrentContextItems(); - HttpContext.Current.Items["altTemplate"] = null; - - HttpContext Context = HttpContext.Current; - StringBuilder queryString = new StringBuilder(); - const string ONE_QS_PARAM = "&{0}={1}"; - foreach (object key in Context.Request.QueryString.Keys) - { - if (!key.ToString().ToLower().Equals("umbpageid") && !key.ToString().ToLower().Equals("alttemplate")) - queryString.Append(string.Format(ONE_QS_PARAM, key, Context.Request.QueryString[key.ToString()])); - } - StringWriter sw = new StringWriter(); + var context = HttpContext.Current; + var queryString = context.Request.QueryString.AllKeys + .ToDictionary(key => key, key => context.Request.QueryString[key]); + var sw = new StringWriter(); try { - Context.Server.Execute( - string.Format("~/default.aspx?umbPageID={0}&alttemplate={1}{2}", - PageId, new template(TemplateId).TemplateAlias, queryString), sw); - + var altTemplate = TemplateId == -1 ? null : (int?)TemplateId; + var templateRenderer = new TemplateRenderer(Umbraco.Web.UmbracoContext.Current) + { + PageId = PageId, + AltTemplate = altTemplate, + QueryStrings = queryString + }; + templateRenderer.Render(sw); } catch (Exception ee) { sw.Write("", ee); } - // update the local page items again - updateLocalContextItems(items, Context); - return sw.ToString(); } @@ -1141,39 +1136,17 @@ namespace umbraco } else { - page p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()); + + var p = new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()); p.RenderPage(TemplateId); - Control c = p.PageContentControl; - StringWriter sw = new StringWriter(); - HtmlTextWriter hw = new HtmlTextWriter(sw); + var c = p.PageContentControl; + var sw = new StringWriter(); + var hw = new HtmlTextWriter(sw); c.RenderControl(hw); - return sw.ToString(); } } - private static System.Collections.Generic.Dictionary getCurrentContextItems() - { - IDictionary items = HttpContext.Current.Items; - System.Collections.Generic.Dictionary currentItems = new Dictionary(); - IDictionaryEnumerator ide = items.GetEnumerator(); - while (ide.MoveNext()) - { - currentItems.Add(ide.Key, ide.Value); - } - return currentItems; - } - - private static void updateLocalContextItems(IDictionary items, HttpContext Context) - { - Context.Items.Clear(); - IDictionaryEnumerator ide = items.GetEnumerator(); - while (ide.MoveNext()) - { - Context.Items.Add(ide.Key, ide.Value); - } - } - /// /// Renders the default template for a specific page. /// @@ -1181,47 +1154,7 @@ namespace umbraco /// The rendered template as a string. public static string RenderTemplate(int PageId) { - if (UmbracoSettings.UseAspNetMasterPages) - { - if (!umbraco.presentation.UmbracoContext.Current.LiveEditingContext.Enabled) - { - System.Collections.Generic.Dictionary items = getCurrentContextItems(); - HttpContext.Current.Items["altTemplate"] = null; - - HttpContext Context = HttpContext.Current; - StringBuilder queryString = new StringBuilder(); - const string ONE_QS_PARAM = "&{0}={1}"; - foreach (object key in Context.Request.QueryString.Keys) - { - if (!key.ToString().ToLower().Equals("umbpageid") && !key.ToString().ToLower().Equals("alttemplate")) - queryString.Append(string.Format(ONE_QS_PARAM, key, Context.Request.QueryString[key.ToString()])); - } - StringWriter sw = new StringWriter(); - try - { - Context.Server.Execute(string.Format("/default.aspx?umbPageID={0}{1}", PageId, queryString), sw); - } - catch (Exception ee) - { - sw.Write("", ee); - } - - // update the local page items again - updateLocalContextItems(items, Context); - - return sw.ToString(); - } - else - { - return "RenderTemplate not supported in Canvas"; - } - } - else - { - return - RenderTemplate(PageId, - new page(((IHasXmlNode)GetXmlNodeById(PageId.ToString()).Current).GetNode()).Template); - } + return RenderTemplate(PageId, -1); } ///