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 } }