/// <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 <UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { var node = umbracoContext.ContentCache.GetById(id); if (node == null) { yield break; } var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); // look for domains, walking up the tree var n = node; var domainUris = domainHelper.DomainsForNode(n.Id, current, false); while (domainUris == null && n != null) // n is null at root { n = n.Parent; // move to parent node domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current, excludeDefault: true); } // no domains = exit if (domainUris == null) { yield break; } foreach (var d in domainUris) { var culture = d?.Culture?.Name; //although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok var route = umbracoContext.ContentCache.GetRouteById(id, culture); if (route == null) { continue; } //need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) var pos = route.IndexOf('/'); var path = pos == 0 ? route : route.Substring(pos); var uri = new Uri(CombinePaths(d.Uri.GetLeftPart(UriPartial.Path), path)); uri = UriUtility.UriFromUmbraco(uri, _globalSettings, _requestSettings); yield return(UrlInfo.Url(uri.ToString(), culture)); } }
private static bool DetectCollision(IContent content, string url, string culture, UmbracoContext umbracoContext, PublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) { // test for collisions on the 'main' url var uri = new Uri(url.TrimEnd('/'), UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri == false) { uri = uri.MakeAbsolute(umbracoContext.CleanedUmbracoUrl); } uri = UriUtility.UriToUmbraco(uri); var pcr = publishedRouter.CreateRequest(umbracoContext, uri); publishedRouter.TryRouteRequest(pcr); urlInfo = null; if (pcr.HasPublishedContent == false) { urlInfo = UrlInfo.Message(textService.Localize("content/routeErrorCannotRoute"), culture); return(true); } if (pcr.IgnorePublishedContentCollisions) { return(false); } if (pcr.PublishedContent.Id != content.Id) { var o = pcr.PublishedContent; var l = new List <string>(); while (o != null) { l.Add(o.Name); o = o.Parent; } l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; urlInfo = UrlInfo.Message(textService.Localize("content/routeError", new[] { s }), culture); return(true); } // no collision return(false); }
internal UrlInfo GetUrlFromRoute(string route, UmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture) { if (string.IsNullOrWhiteSpace(route)) { _logger.Debug <DefaultUrlProvider>("Couldn't find any page with nodeId={NodeId}. 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 : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, int.Parse(route.Substring(0, pos)), current, culture); // assemble the URL from domainUri (maybe null) and path var url = AssembleUrl(domainUri, path, current, mode).ToString(); return(UrlInfo.Url(url, culture)); }
/// <summary> /// Gets the Urls of the content item. /// </summary> /// <remarks> /// <para>Use when displaying Urls. If errors occur when generating the Urls, they will show in the list.</para> /// <para>Contains all the Urls that we can figure out (based upon domains, etc).</para> /// </remarks> public static IEnumerable <UrlInfo> GetContentUrls(this IContent content, PublishedRouter publishedRouter, UmbracoContext umbracoContext, ILocalizationService localizationService, ILocalizedTextService textService, IContentService contentService, ILogger logger) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if (publishedRouter == null) { throw new ArgumentNullException(nameof(publishedRouter)); } if (umbracoContext == null) { throw new ArgumentNullException(nameof(umbracoContext)); } if (localizationService == null) { throw new ArgumentNullException(nameof(localizationService)); } if (textService == null) { throw new ArgumentNullException(nameof(textService)); } if (contentService == null) { throw new ArgumentNullException(nameof(contentService)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (content.Published == false) { yield return(UrlInfo.Message(textService.Localize("content/itemNotPublished"))); yield break; } // build a list of urls, for the back-office // which will contain // - the 'main' urls, which is what .Url would return, for each culture // - the 'other' urls we know (based upon domains, etc) // // need to work through each installed culture: // on invariant nodes, each culture returns the same url segment but, // we don't know if the branch to this content is invariant, so we need to ask // for URLs for all cultures. // and, not only for those assigned to domains in the branch, because we want // to show what GetUrl() would return, for every culture. var urls = new HashSet <UrlInfo>(); var cultures = localizationService.GetAllLanguages().Select(x => x.IsoCode).ToList(); //get all URLs for all cultures //in a HashSet, so de-duplicates too foreach (var cultureUrl in GetContentUrlsByCulture(content, cultures, publishedRouter, umbracoContext, contentService, textService, logger)) { urls.Add(cultureUrl); } //return the real urls first, then the messages foreach (var urlGroup in urls.GroupBy(x => x.IsUrl).OrderByDescending(x => x.Key)) { //in some cases there will be the same URL for multiple cultures: // * The entire branch is invariant // * If there are less domain/cultures assigned to the branch than the number of cultures/languages installed foreach (var dUrl in urlGroup.DistinctBy(x => x.Text.ToUpperInvariant()).OrderBy(x => x.Text).ThenBy(x => x.Culture)) { yield return(dUrl); } } // get the 'other' urls - ie not what you'd get with GetUrl() but urls that would route to the document, nevertheless. // for these 'other' urls, we don't check whether they are routable, collide, anything - we just report them. foreach (var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) { if (urls.Add(otherUrl)) //avoid duplicates { yield return(otherUrl); } } }
/// <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 IEnumerable <UrlInfo> GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { var node = umbracoContext.Content.GetById(id); if (node == null) { yield break; } if (!node.HasProperty(Constants.Conventions.Content.UrlAlias)) { yield break; } // look for domains, walking up the tree var n = node; var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, false); while (domainUris == null && n != null) // n is null at root { // move to parent node n = n.Parent; domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainHelper, n.Id, current, excludeDefault: false); } // determine whether the alias property varies var varies = node.GetProperty(Constants.Conventions.Content.UrlAlias).PropertyType.VariesByCulture(); if (domainUris == null) { // no domain // if the property is invariant, then url "/<alias>" is ok // if the property varies, then what are we supposed to do? // the content finder may work, depending on the 'current' culture, // but there's no way we can return something meaningful here if (varies) { yield break; } var umbracoUrlName = node.Value <string>(Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (aliases == null || aliases.Any() == false) { yield break; } foreach (var alias in aliases.Distinct()) { var path = "/" + alias; var uri = new Uri(path, UriKind.Relative); yield return(UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString())); } } else { // some domains: one url per domain, which is "<domain>/<alias>" foreach (var domainUri in domainUris) { // if the property is invariant, get the invariant value, url is "<domain>/<invariant-alias>" // if the property varies, get the variant value, url is "<domain>/<variant-alias>" // but! only if the culture is published, else ignore if (varies && !node.HasCulture(domainUri.Culture.Name)) { continue; } var umbracoUrlName = varies ? node.Value <string>(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name) : node.Value <string>(Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (aliases == null || aliases.Any() == false) { continue; } foreach (var alias in aliases.Distinct()) { var path = "/" + alias; var uri = new Uri(CombinePaths(domainUri.Uri.GetLeftPart(UriPartial.Path), path)); yield return(UrlInfo.Url(UriUtility.UriFromUmbraco(uri, _globalSettings, _requestConfig).ToString(), domainUri.Culture.Name)); } } } }
private static bool DetectCollision(ILogger logger, IContent content, string url, string culture, UmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, out UrlInfo urlInfo) { // test for collisions on the 'main' URL var uri = new Uri(url.TrimEnd(Constants.CharArrays.ForwardSlash), UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri == false) { uri = uri.MakeAbsolute(umbracoContext.CleanedUmbracoUrl); } uri = UriUtility.UriToUmbraco(uri); var pcr = publishedRouter.CreateRequest(umbracoContext, uri); publishedRouter.TryRouteRequest(pcr); urlInfo = null; if (pcr.HasPublishedContent == false) { var logMsg = nameof(DetectCollision) + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; if (pcr.IgnorePublishedContentCollisions) { logger.Debug(typeof(UrlProviderExtensions), logMsg, url, uri, culture); } else { logger.Warn(typeof(UrlProviderExtensions), logMsg, url, uri, culture); } urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture); return(true); } if (pcr.IgnorePublishedContentCollisions) { return(false); } if (pcr.PublishedContent.Id != content.Id) { var o = pcr.PublishedContent; var l = new List <string>(); while (o != null) { l.Add(o.Name()); o = o.Parent; } l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; urlInfo = UrlInfo.Message(textService.Localize("content", "routeError", new[] { s }), culture); return(true); } // no collision return(false); }