diff --git a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs index 9d61f315ce..79cab7e194 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlProviderTests.cs @@ -155,7 +155,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177)); SettingsForTests.UseDomainPrefixes = false; - routingContext.UrlProvider.EnforceAbsoluteUrls = true; + routingContext.UrlProvider.Mode = UrlProviderMode.Absolute; Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", routingContext.UrlProvider.GetUrl(1177)); } @@ -172,7 +172,7 @@ namespace Umbraco.Tests.Routing SettingsForTests.UseDomainPrefixes = true; Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999)); SettingsForTests.UseDomainPrefixes = false; - routingContext.UrlProvider.EnforceAbsoluteUrls = true; + routingContext.UrlProvider.Mode = UrlProviderMode.Absolute; Assert.AreEqual("#", routingContext.UrlProvider.GetUrl(999999)); } } diff --git a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs index 6eb981fb22..88f17deb46 100644 --- a/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs +++ b/src/Umbraco.Tests/Routing/NiceUrlsProviderWithDomainsTests.cs @@ -368,7 +368,7 @@ namespace Umbraco.Tests.Routing Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311)); SettingsForTests.UseDomainPrefixes = false; - routingContext.UrlProvider.EnforceAbsoluteUrls = true; + routingContext.UrlProvider.Mode = UrlProviderMode.Absolute; Assert.AreEqual("http://domain1.com/en/1001-1-1/", routingContext.UrlProvider.GetUrl(100111)); Assert.AreEqual("http://domain3.com/en/1003-1-1/", routingContext.UrlProvider.GetUrl(100311)); } diff --git a/src/Umbraco.Web/Configuration/WebRouting.cs b/src/Umbraco.Web/Configuration/WebRouting.cs index d128c029de..e05a8e3ccd 100644 --- a/src/Umbraco.Web/Configuration/WebRouting.cs +++ b/src/Umbraco.Web/Configuration/WebRouting.cs @@ -11,14 +11,17 @@ namespace Umbraco.Web.Configuration internal class WebRouting : UmbracoConfigurationSection { private const string KeyTrySkipIisCustomErrors = "trySkipIisCustomErrors"; + private const string KeyUrlProviderMode = "urlProviderMode"; private bool? _trySkipIisCustomErrors; + private Routing.UrlProviderMode? _urlProviderMode; internal protected override void ResetSection() { base.ResetSection(); _trySkipIisCustomErrors = null; + _urlProviderMode = null; } /// @@ -35,5 +38,21 @@ namespace Umbraco.Web.Configuration } internal set { _trySkipIisCustomErrors = value; } } + + /// + /// Gets or sets the url provider mode. + /// + [ConfigurationProperty(KeyUrlProviderMode, DefaultValue = Routing.UrlProviderMode.AutoLegacy, IsRequired = false)] + [TypeConverter(typeof(CaseInsensitiveEnumConfigConverter))] + public Routing.UrlProviderMode UrlProviderMode + { + get + { + return _urlProviderMode ?? (IsPresent + ? (Routing.UrlProviderMode)this[KeyUrlProviderMode] + : Routing.UrlProviderMode.Auto); + } + internal set { _urlProviderMode = value; } + } } } diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 6821e3c1fc..8903ddb22d 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -23,14 +23,14 @@ namespace Umbraco.Web.Routing /// The content cache. /// The published content id. /// The current absolute url. - /// A value indicating whether the url should be absolute in any case. + /// The url mode. /// The url for the published content. /// /// The url is absolute or relative depending on url indicated by current and settings, unless /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it should return null. /// - public string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute) + public string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode) { return null; // we have nothing to say } diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index dd0f8c73e2..9b762e328c 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -23,14 +23,13 @@ namespace Umbraco.Web.Routing /// The content cache. /// The published content id. /// The current absolute url. - /// A value indicating whether the url should be absolute in any case. + /// The url mode. /// The url for the published content. /// - /// The url is absolute or relative depending on url indicated by current and settings, unless - /// absolute is true, in which case the url is always absolute. + /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute) + public virtual string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode) { DomainAndUri domainUri; string path; @@ -49,7 +48,7 @@ namespace Umbraco.Web.Routing { // there was a route in the cache - extract domainUri and path // route is / or / - int pos = route.IndexOf('/'); + var pos = route.IndexOf('/'); path = pos == 0 ? route : route.Substring(pos); domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); } @@ -97,7 +96,7 @@ namespace Umbraco.Web.Routing } // assemble the url from domainUri (maybe null) and path - return AssembleUrl(domainUri, path, current, absolute).ToString(); + return AssembleUrl(domainUri, path, current, mode).ToString(); } #endregion @@ -185,27 +184,65 @@ namespace Umbraco.Web.Routing #region Utilities - Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, bool absolute) + Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode) { Uri uri; - if (domainUri == null) + // ignore vdir at that point, UriFromUmbraco will do it + + if (mode == UrlProviderMode.AutoLegacy) { - // no domain was found : return an absolute or relative url - // ignore vdir at that point - if (!absolute || current == null) - uri = new Uri(path, UriKind.Relative); - else - uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path); + mode = Core.Configuration.UmbracoSettings.UseDomainPrefixes + ? UrlProviderMode.Absolute + : UrlProviderMode.Auto; } - else + + if (mode == UrlProviderMode.AutoLegacy) { - // a domain was found : return an absolute or relative url - // ignore vdir at that point - if (!absolute && current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) - uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative); // relative - else - uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); // absolute + mode = Core.Configuration.UmbracoSettings.UseDomainPrefixes + ? UrlProviderMode.Absolute + : UrlProviderMode.Auto; + } + + if (domainUri == null) // no domain was found + { + if (current == null) + mode = UrlProviderMode.Relative; // best we can do + + switch (mode) + { + case UrlProviderMode.Absolute: + uri = new Uri(current.GetLeftPart(UriPartial.Authority) + path); + break; + case UrlProviderMode.Relative: + case UrlProviderMode.Auto: + uri = new Uri(path, UriKind.Relative); + break; + default: + throw new ArgumentOutOfRangeException("mode"); + } + } + else // a domain was found + { + if (mode == UrlProviderMode.Auto) + { + if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) + mode = UrlProviderMode.Relative; + else + mode = UrlProviderMode.Absolute; + } + + switch (mode) + { + case UrlProviderMode.Absolute: + uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); + break; + case UrlProviderMode.Relative: + uri = new Uri(CombinePaths(domainUri.Uri.AbsolutePath, path), UriKind.Relative); + break; + default: + throw new ArgumentOutOfRangeException("mode"); + } } // UriFromUmbraco will handle vdir diff --git a/src/Umbraco.Web/Routing/IUrlProvider.cs b/src/Umbraco.Web/Routing/IUrlProvider.cs index 2a14b56cbb..7cd0924654 100644 --- a/src/Umbraco.Web/Routing/IUrlProvider.cs +++ b/src/Umbraco.Web/Routing/IUrlProvider.cs @@ -15,14 +15,13 @@ namespace Umbraco.Web.Routing /// The content cache. /// The published content id. /// The current absolute url. - /// A value indicating whether the url should be absolute in any case. + /// The url mode. /// The url for the published content. /// - /// The url is absolute or relative depending on url indicated by current and settings, unless - /// absolute is true, in which case the url is always absolute. + /// The url is absolute or relative depending on mode and on current. /// If the provider is unable to provide a url, it should return null. /// - string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, bool absolute); + string GetUrl(UmbracoContext umbracoContext, IPublishedContentStore contentCache, int id, Uri current, UrlProviderMode mode); /// /// Gets the other urls of a published content. diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 6b9b5e9778..ba71e4f31c 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Routing _umbracoContext = umbracoContext; _contentCache = contentCache; _urlProviders = urlProviders; - EnforceAbsoluteUrls = false; + Mode = UmbracoSettings.For().UrlProviderMode; } private readonly UmbracoContext _umbracoContext; @@ -32,9 +32,9 @@ namespace Umbraco.Web.Routing private readonly IEnumerable _urlProviders; /// - /// Gets or sets a value indicating whether the provider should enforce absolute urls. + /// Gets or sets the provider url mode. /// - public bool EnforceAbsoluteUrls { get; set; } + public UrlProviderMode Mode { get; set; } #endregion @@ -46,13 +46,12 @@ namespace Umbraco.Web.Routing /// The published content identifier. /// The url for the published content. /// - /// The url is absolute or relative depending on the current url, settings, and options. + /// The url is absolute or relative depending on Mode and on the current url. /// If the provider is unable to provide a url, it returns "#". /// public string GetUrl(int id) { - var absolute = UmbracoSettings.UseDomainPrefixes | EnforceAbsoluteUrls; - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute); + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, Mode); } /// @@ -62,14 +61,14 @@ namespace Umbraco.Web.Routing /// A value indicating whether the url should be absolute in any case. /// The url for the published content. /// - /// The url is absolute or relative depending on the current url and settings, unless absolute is true, - /// in which case the url is always absolute. + /// The url is absolute or relative depending on Mode and on current, unless + /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// public string GetUrl(int id, bool absolute) { - absolute = absolute | EnforceAbsoluteUrls; - return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, absolute); + var mode = absolute ? UrlProviderMode.Absolute : Mode; + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode); } /// @@ -80,14 +79,46 @@ namespace Umbraco.Web.Routing /// A value indicating whether the url should be absolute in any case. /// The url for the published content. /// - /// The url is absolute or relative depending on url indicated by current and settings, unless + /// The url is absolute or relative depending on Mode and on current, unless /// absolute is true, in which case the url is always absolute. /// If the provider is unable to provide a url, it returns "#". /// public string GetUrl(int id, Uri current, bool absolute) { - absolute = absolute | EnforceAbsoluteUrls; - var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, _contentCache, id, current, absolute)).FirstOrDefault(u => u != null); + var mode = absolute ? UrlProviderMode.Absolute : Mode; + return GetUrl(id, current, mode); + } + + /// + /// Gets the nice url of a published content. + /// + /// The published content identifier. + /// The url mode. + /// The url for the published content. + /// + /// The url is absolute or relative depending on mode and on the current url. + /// If the provider is unable to provide a url, it returns "#". + /// + public string GetUrl(int id, UrlProviderMode mode) + { + return GetUrl(id, _umbracoContext.CleanedUmbracoUrl, mode); + } + + /// + /// Gets the nice url of a published content. + /// + /// The published content id. + /// The current absolute url. + /// The url mode. + /// The url for the published content. + /// + /// The url is absolute or relative depending on mode and on current. + /// If the provider is unable to provide a url, it returns "#". + /// + public string GetUrl(int id, Uri current, UrlProviderMode mode) + { + var url = _urlProviders.Select(provider => provider.GetUrl(_umbracoContext, _contentCache, id, current, mode)) + .FirstOrDefault(u => u != null); return url ?? "#"; // legacy wants this } diff --git a/src/Umbraco.Web/Routing/UrlProviderMode.cs b/src/Umbraco.Web/Routing/UrlProviderMode.cs new file mode 100644 index 0000000000..8a26eac0c2 --- /dev/null +++ b/src/Umbraco.Web/Routing/UrlProviderMode.cs @@ -0,0 +1,36 @@ +namespace Umbraco.Web.Routing +{ + /// + /// Specifies the type of urls that the url provider should produce. + /// + /// + /// The AutoLegacy option is equivalent to Auto but it also respects the legacy useDomainPrefixes setting. + /// When that setting is true, then all urls are absolute. Otherwise, urls will be relative or absolute, depending on hostnames. + /// The Relative option can lead to invalid results when combined with hostnames, but it is the only way to reproduce + /// the true, pre-4.10, always-relative behavior of Umbraco. + /// For the time being, the default option is AutoLegacy although in the future it will be Auto. + /// + internal enum UrlProviderMode + { + /// + /// Indicates that the url provider should determine automatically whether to return relative or absolute urls, + /// and also respect the legacy useDomainPrefixes setting. + /// + AutoLegacy, + + /// + /// Indicates that the url provider should produce relative urls exclusively. + /// + Relative, + + /// + /// Indicates that the url provider should produce absolute urls exclusively. + /// + Absolute, + + /// + /// Indicates that the url provider should determine automatically whether to return relative or absolute urls. + /// + Auto + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 4ad274d4b9..a3b998c8e1 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -325,6 +325,7 @@ +