diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
index 9eb6d02aa7..c59d63d42e 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/IWebRoutingSection.cs
@@ -8,6 +8,8 @@
bool DisableAlternativeTemplates { get; }
+ bool ValidateAlternativeTemplates { get; }
+
bool DisableFindContentByIdPath { get; }
bool DisableRedirectUrlTracking { get; }
diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
index 82f5d46b28..ed133e5f61 100644
--- a/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
+++ b/src/Umbraco.Core/Configuration/UmbracoSettings/WebRoutingElement.cs
@@ -1,4 +1,5 @@
-using System.Configuration;
+using System;
+using System.Configuration;
namespace Umbraco.Core.Configuration.UmbracoSettings
{
@@ -21,6 +22,13 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
{
get { return (bool) base["disableAlternativeTemplates"]; }
}
+
+ [ConfigurationProperty("validateAlternativeTemplates", DefaultValue = "false")]
+ public bool ValidateAlternativeTemplates
+ {
+ get { return (bool)base["validateAlternativeTemplates"]; }
+ }
+
[ConfigurationProperty("disableFindContentByIdPath", DefaultValue = "false")]
public bool DisableFindContentByIdPath
{
diff --git a/src/Umbraco.Core/Models/ContentType.cs b/src/Umbraco.Core/Models/ContentType.cs
index 568d80ccb7..549ccb24e8 100644
--- a/src/Umbraco.Core/Models/ContentType.cs
+++ b/src/Umbraco.Core/Models/ContentType.cs
@@ -102,6 +102,28 @@ namespace Umbraco.Core.Models
}
}
+ ///
+ /// Determines if AllowedTemplates contains templateId
+ ///
+ /// The template id to check
+ /// True if AllowedTemplates contains the templateId else False
+ public bool IsAllowedTemplate(int templateId)
+ {
+ var allowedTemplates = AllowedTemplates ?? new ITemplate[0];
+ return allowedTemplates.Any(t => t.Id == templateId);
+ }
+
+ ///
+ /// Determines if AllowedTemplates contains templateId
+ ///
+ /// The template alias to check
+ /// True if AllowedTemplates contains the templateAlias else False
+ public bool IsAllowedTemplate(string templateAlias)
+ {
+ var allowedTemplates = AllowedTemplates ?? new ITemplate[0];
+ return allowedTemplates.Any(t => t.Alias.Equals(templateAlias, StringComparison.InvariantCultureIgnoreCase));
+ }
+
///
/// Sets the default template for the ContentType
///
diff --git a/src/Umbraco.Core/Models/IContentType.cs b/src/Umbraco.Core/Models/IContentType.cs
index 766a8eec81..356a5acccd 100644
--- a/src/Umbraco.Core/Models/IContentType.cs
+++ b/src/Umbraco.Core/Models/IContentType.cs
@@ -17,6 +17,20 @@ namespace Umbraco.Core.Models
///
IEnumerable AllowedTemplates { get; set; }
+ ///
+ /// Determines if AllowedTemplates contains templateId
+ ///
+ /// The template id to check
+ /// True if AllowedTemplates contains the templateId else False
+ bool IsAllowedTemplate(int templateId);
+
+ ///
+ /// Determines if AllowedTemplates contains templateId
+ ///
+ /// The template alias to check
+ /// True if AllowedTemplates contains the templateAlias else False
+ bool IsAllowedTemplate(string templateAlias);
+
///
/// Sets the default template for the ContentType
///
diff --git a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs
index 1e568c608e..4c77fff5bc 100644
--- a/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs
+++ b/src/Umbraco.Tests/Configurations/UmbracoSettings/WebRoutingElementDefaultTests.cs
@@ -23,6 +23,12 @@ namespace Umbraco.Tests.Configurations.UmbracoSettings
Assert.IsTrue(SettingsSection.WebRouting.DisableAlternativeTemplates == false);
}
+ [Test]
+ public void ValidateAlternativeTemplates()
+ {
+ Assert.IsTrue(SettingsSection.WebRouting.ValidateAlternativeTemplates == false);
+ }
+
[Test]
public void DisableFindContentByIdPath()
{
diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
index 27f5fc97c7..88d350225b 100644
--- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
+++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config
@@ -152,6 +152,12 @@
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
"About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
+ @validateAlternativeTemplates
+ By default you can add a altTemplate querystring or append a template name to the current URL which
+ will make Umbraco render the content on the current page with the template you requested, for example:
+ http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
+ "About Us" page with a template with the alias Home. Setting this setting to true will ensure that
+ only templates that have been permitted on the document type will be allowed
@disableFindContentByIdPath
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
@@ -163,7 +169,7 @@
-->
diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config
index 2b2fdf2674..aa8225b082 100644
--- a/src/Umbraco.Web.UI/config/umbracoSettings.config
+++ b/src/Umbraco.Web.UI/config/umbracoSettings.config
@@ -304,7 +304,13 @@
By default you can add a altTemplate querystring or append a template name to the current URL which
will make Umbraco render the content on the current page with the template you requested, for example:
http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
- "About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
+ "About Us" page with a template with the alias Home. Setting this setting to true stops that behavior
+ @validateAlternativeTemplates
+ By default you can add a altTemplate querystring or append a template name to the current URL which
+ will make Umbraco render the content on the current page with the template you requested, for example:
+ http://mysite.com/about-us/?altTemplate=Home and http://mysite.com/about-us/Home would render the
+ "About Us" page with a template with the alias Home. Setting this setting to true will ensure that
+ only templates that have been permitted on the document type will be allowed
@disableFindContentByIdPath
By default you can call any content Id in the url and show the content with that id, for example:
http://mysite.com/1092 or http://mysite.com/1092.aspx would render the content with id 1092. Setting
@@ -316,7 +322,7 @@
-->
diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs
index f1dd008e38..c80f41d9fc 100644
--- a/src/Umbraco.Web/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web/PublishedContentExtensions.cs
@@ -6,11 +6,12 @@ using System.Globalization;
using System.Linq;
using System.Web;
using Examine.LuceneEngine.SearchCriteria;
+using Umbraco.Core;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Web.Models;
-using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Web.Routing;
using ContentType = umbraco.cms.businesslogic.ContentType;
@@ -130,6 +131,32 @@ namespace Umbraco.Web
{
var template = ApplicationContext.Current.Services.FileService.GetTemplate(content.TemplateId);
return template == null ? string.Empty : template.Alias;
+ }
+
+ public static bool IsAllowedTemplate(this IPublishedContent content, int templateId)
+ {
+ if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == true)
+ return content.TemplateId == templateId;
+
+ if (content.TemplateId != templateId && UmbracoConfig.For.UmbracoSettings().WebRouting.ValidateAlternativeTemplates == true)
+ {
+ var publishedContentContentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(content.ContentType.Id);
+ if (publishedContentContentType == null)
+ throw new NullReferenceException("No content type returned for published content (contentType='" + content.ContentType.Id + "')");
+
+ return publishedContentContentType.IsAllowedTemplate(templateId);
+ }
+
+ return true;
+ }
+ public static bool IsAllowedTemplate(this IPublishedContent content, string templateAlias)
+ {
+ var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateAlias);
+ var isAllowedTemplate = (template != null) ?
+ content.IsAllowedTemplate(template.Id) :
+ false;
+
+ return isAllowedTemplate;
}
#endregion
diff --git a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs
index 246b109dea..f19886c4f2 100644
--- a/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs
+++ b/src/Umbraco.Web/Routing/ContentFinderByNiceUrlAndTemplate.cs
@@ -22,6 +22,8 @@ namespace Umbraco.Web.Routing
/// If successful, also assigns the template.
public override bool TryFindContent(PublishedContentRequest docRequest)
{
+ const string tracePrefix = "ContentFinderByNiceUrlAndTemplate: ";
+
IPublishedContent node = null;
string path = docRequest.Uri.GetAbsolutePathDecoded();
@@ -42,8 +44,16 @@ namespace Umbraco.Web.Routing
var route = docRequest.HasDomain ? (docRequest.Domain.RootNodeId.ToString() + path) : path;
node = FindContent(docRequest, route);
- if (UmbracoConfig.For.UmbracoSettings().WebRouting.DisableAlternativeTemplates == false && node != null)
+ if (node.IsAllowedTemplate(template.Id))
+ {
docRequest.TemplateModel = template;
+ }
+ else
+ {
+ LogHelper.Warn("Configuration settings prevent template \"{0}\" from showing for node \"{1}\"", () => templateAlias, () => node.Id);
+ docRequest.PublishedContent = null;
+ node = null;
+ }
}
else
{
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
index afbfe1c4f6..217f407f02 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
@@ -11,6 +11,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
+using Umbraco.Core.Models;
using Umbraco.Core.Security;
using umbraco;
@@ -651,9 +652,8 @@ namespace Umbraco.Web.Routing
// only if the published content is the initial once, else the alternate template
// does not apply
// + optionnally, apply the alternate template on internal redirects
- var useAltTemplate = _webRoutingSection.DisableAlternativeTemplates == false
- && (_pcr.IsInitialPublishedContent
- || (_webRoutingSection.InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent));
+ var useAltTemplate = _pcr.IsInitialPublishedContent
+ || (_webRoutingSection.InternalRedirectPreservesTemplate && _pcr.IsInternalRedirectPublishedContent);
string altTemplate = useAltTemplate
? _routingContext.UmbracoContext.HttpContext.Request[Constants.Conventions.Url.AltTemplate]
: null;
@@ -675,20 +675,11 @@ namespace Umbraco.Web.Routing
var templateId = _pcr.PublishedContent.TemplateId;
- if (templateId > 0)
- {
- ProfilingLogger.Logger.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateId);
- var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateId);
- if (template == null)
- throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render");
- _pcr.TemplateModel = template;
- ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias);
- }
- else
- {
- ProfilingLogger.Logger.Debug("{0}No specified template.", () => tracePrefix);
- }
- }
+ // This code was moved to GetTemplateModel to allow the same functionality on a failed altTemplate (U4-8550)
+ // The only change is a diffent logger prefix and null will be set to _pcr.TemplateModel if the templateId is <= 0
+ // rather than no set taking place
+ _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId);
+ }
else
{
// we have an alternate template specified. lookup the template with that alias
@@ -701,16 +692,19 @@ namespace Umbraco.Web.Routing
ProfilingLogger.Logger.Debug("{0}Has a template already, but also an alternate template.", () => tracePrefix);
ProfilingLogger.Logger.Debug("{0}Look for alternate template alias=\"{1}\"", () => tracePrefix, () => altTemplate);
- var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate);
- if (template != null)
- {
- _pcr.TemplateModel = template;
- ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias);
- }
- else
- {
- ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate);
- }
+ if (_pcr.PublishedContent.IsAllowedTemplate(altTemplate))
+ {
+ var template = ApplicationContext.Current.Services.FileService.GetTemplate(altTemplate);
+ if (template != null)
+ _pcr.TemplateModel = template;
+ else
+ ProfilingLogger.Logger.Debug("{0}The template with alias=\"{1}\" does not exist, ignoring.", () => tracePrefix, () => altTemplate);
+ }
+ else
+ {
+ LogHelper.Warn("{0}Configuration settings prevent template \"{1}\" from showing for node \"{2}\"", () => tracePrefix, () => altTemplate, () => _pcr.PublishedContent.Id);
+ _pcr.TemplateModel = GetTemplateModel(_pcr.PublishedContent.TemplateId);
+ }
}
if (_pcr.HasTemplate == false)
@@ -732,11 +726,31 @@ namespace Umbraco.Web.Routing
}
}
- ///
- /// Follows external redirection through umbracoRedirect document property.
- ///
- /// As per legacy, if the redirect does not work, we just ignore it.
- private void FollowExternalRedirect()
+ private ITemplate GetTemplateModel(int templateId)
+ {
+ const string tracePrefix = "GetTemplateModel: ";
+
+ if (templateId > 0)
+ {
+ ProfilingLogger.Logger.Debug("{0}Look for template id={1}", () => tracePrefix, () => templateId);
+ var template = ApplicationContext.Current.Services.FileService.GetTemplate(templateId);
+ if (template == null)
+ throw new InvalidOperationException("The template with Id " + templateId + " does not exist, the page cannot render");
+ ProfilingLogger.Logger.Debug("{0}Got template id={1} alias=\"{2}\"", () => tracePrefix, () => template.Id, () => template.Alias);
+ return template;
+ }
+ else
+ {
+ ProfilingLogger.Logger.Debug("{0}No specified template.", () => tracePrefix);
+ }
+ return null;
+ }
+
+ ///
+ /// Follows external redirection through umbracoRedirect document property.
+ ///
+ /// As per legacy, if the redirect does not work, we just ignore it.
+ private void FollowExternalRedirect()
{
if (_pcr.HasPublishedContent == false) return;