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
{
///
/// Provides urls.
///
public class DefaultUrlProvider : IUrlProvider
{
#region GetUrl
///
/// Gets the nice url of a published content.
///
/// The Umbraco context.
/// 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 should return null.
///
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.Warn(
"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 / or /
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
///
/// Gets the other urls of a published content.
///
/// The Umbraco context.
/// The published content id.
/// The current absolute url.
/// The other urls for the published content.
///
/// Other urls are those that GetUrl would not return in the current context, but would be valid
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).
///
public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current)
{
// will not use cache if previewing
var route = umbracoContext.ContentCache.GetRouteById(id);
if (string.IsNullOrWhiteSpace(route))
{
LogHelper.Warn(
"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 / or /
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 AssembleUrls(IEnumerable domainUris, string path)
{
// no domain == no "other" url
if (domainUris == null)
return Enumerable.Empty();
// 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
}
}