Files
Umbraco-CMS/src/Umbraco.Web/Routing/DefaultUrlProvider.cs

185 lines
7.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Web.PublishedCache;
using umbraco.cms.businesslogic.web;
namespace Umbraco.Web.Routing
{
/// <summary>
/// Provides urls.
/// </summary>
public class DefaultUrlProvider : IUrlProvider
{
#region GetUrl
/// <summary>
/// Gets the nice url of a published content.
/// </summary>
/// <param name="umbracoContext">The Umbraco context.</param>
/// <param name="id">The published content id.</param>
/// <param name="current">The current absolute url.</param>
/// <param name="mode">The url mode.</param>
/// <returns>The url for the published content.</returns>
/// <remarks>
/// <para>The url is absolute or relative depending on <c>mode</c> and on <c>current</c>.</para>
/// <para>If the provider is unable to provide a url, it should return <c>null</c>.</para>
/// </remarks>
public virtual string GetUrl(UmbracoContext umbracoContext, int id, Uri current, UrlProviderMode mode)
{
if (!current.IsAbsoluteUri)
throw new ArgumentException("Current url must be absolute.", "current");
// will not use cache if previewing
var route = umbracoContext.ContentCache.GetRouteById(id);
if (string.IsNullOrWhiteSpace(route))
{
LogHelper.Debug<DefaultUrlProvider>(
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
() => id);
return null;
}
// extract domainUri and path
// route is /<path> or <domainRootId>/<path>
var pos = route.IndexOf('/');
var path = pos == 0 ? route : route.Substring(pos);
var domainUri = pos == 0 ? null : DomainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current);
// assemble the url from domainUri (maybe null) and path
return AssembleUrl(domainUri, path, current, mode).ToString();
}
#endregion
#region GetOtherUrls
/// <summary>
/// Gets the other urls of a published content.
/// </summary>
/// <param name="umbracoContext">The Umbraco context.</param>
/// <param name="id">The published content id.</param>
/// <param name="current">The current absolute url.</param>
/// <returns>The other urls for the published content.</returns>
/// <remarks>
/// <para>Other urls are those that <c>GetUrl</c> would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
/// </remarks>
public virtual IEnumerable<string> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
// will not use cache if previewing
var route = umbracoContext.ContentCache.GetRouteById(id);
if (string.IsNullOrWhiteSpace(route))
{
LogHelper.Debug<DefaultUrlProvider>(
"Couldn't find any page with nodeId={0}. This is most likely caused by the page not being published.",
() => id);
return null;
}
// extract domainUri and path
// route is /<path> or <domainRootId>/<path>
var pos = route.IndexOf('/');
var path = pos == 0 ? route : route.Substring(pos);
var domainUris = pos == 0 ? null : DomainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current);
// assemble the alternate urls from domainUris (maybe empty) and path
return AssembleUrls(domainUris, path).Select(uri => uri.ToString());
}
#endregion
#region Utilities
Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode)
{
Uri uri;
// ignore vdir at that point, UriFromUmbraco will do it
if (mode == UrlProviderMode.AutoLegacy)
{
mode = UmbracoConfig.For.UmbracoSettings().RequestHandler.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
// meaning it will add vdir into domain urls too!
return UriUtility.UriFromUmbraco(uri);
}
string CombinePaths(string path1, string path2)
{
string path = path1.TrimEnd('/') + path2;
return path == "/" ? path : path.TrimEnd('/');
}
// always build absolute urls unless we really cannot
IEnumerable<Uri> AssembleUrls(IEnumerable<DomainAndUri> domainUris, string path)
{
// no domain == no "other" url
if (domainUris == null)
return Enumerable.Empty<Uri>();
// if no domain was found and then we have no "other" url
// else return absolute urls, ignoring vdir at that point
var uris = domainUris.Select(domainUri => new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)));
// UriFromUmbraco will handle vdir
// meaning it will add vdir into domain urls too!
return uris.Select(UriUtility.UriFromUmbraco);
}
#endregion
}
}