private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, ILocalizedTextService textService) { // document has a published version yet its url is "#" => a parent must be // unpublished, walk up the tree until we find it, and report. var parent = content; do { parent = parent.ParentId > 0 ? contentService.GetParent(parent) : null; }while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); if (parent == null) // oops, internal error { return(UrlInfo.Message(textService.Localize("content/parentNotPublishedAnomaly"), culture)); } else if (!parent.Published) // totally not published { return(UrlInfo.Message(textService.Localize("content/parentNotPublished", new[] { parent.Name }), culture)); } else // culture not published { return(UrlInfo.Message(textService.Localize("content/parentCultureNotPublished", new[] { parent.Name }), culture)); } }
/// <summary> /// Tries to return a <see cref="UrlInfo"/> for each culture for the content while detecting collisions/errors /// </summary> /// <param name="content"></param> /// <param name="cultures"></param> /// <param name="publishedRouter"></param> /// <param name="umbracoContext"></param> /// <param name="contentService"></param> /// <param name="textService"></param> /// <param name="logger"></param> /// <returns></returns> private static IEnumerable <UrlInfo> GetContentUrlsByCulture(IContent content, IEnumerable <string> cultures, PublishedRouter publishedRouter, UmbracoContext umbracoContext, IContentService contentService, ILocalizedTextService textService, ILogger logger) { foreach (var culture in cultures) { // if content is variant, and culture is not published, skip if (content.ContentType.VariesByCulture() && !content.IsCulturePublished(culture)) { continue; } // if it's variant and culture is published, or if it's invariant, proceed string url; try { url = umbracoContext.UrlProvider.GetUrl(content.Id, culture); } catch (Exception ex) { logger.Error <UrlProvider>(ex, "GetUrl exception."); url = "#ex"; } switch (url) { // deal with 'could not get the url' case "#": yield return(HandleCouldNotGetUrl(content, culture, contentService, textService)); break; // deal with exceptions case "#ex": yield return(UrlInfo.Message(textService.Localize("content/getUrlException"), culture)); break; // got a url, deal with collisions, add url default: if (DetectCollision(content, url, culture, umbracoContext, publishedRouter, textService, out var urlInfo)) // detect collisions, etc { yield return(urlInfo); } else { yield return(UrlInfo.Url(url, culture)); } break; } } }
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); }
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); }
/// <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); } } }