/// <summary> /// This will return the URL that is returned by the assigned custom <see cref="IPublishedContent"/> if this is a custom route /// </summary> public UrlInfo GetUrl(UmbracoContext umbracoContext, IPublishedContent content, UrlProviderMode mode, string culture, Uri current) { if (umbracoContext?.PublishedRequest?.PublishedContent == null) { return(null); } if (umbracoContext.HttpContext?.Request?.RequestContext?.RouteData?.DataTokens == null) { return(null); } if (umbracoContext.HttpContext.Request.RequestContext.RouteData.DataTokens.ContainsKey(Core.Constants.Web.CustomRouteDataToken) == false) { return(null); } //If we get this far, it means it's a custom route with published content assigned, check if the id being requested for is the same id as the assigned published content //NOTE: This looks like it might cause an infinite loop because PublishedContentBase.Url calls into UmbracoContext.Current.UrlProvider.GetUrl which calls back into the IUrlProvider pipeline // but the specific purpose of this is that a developer is using their own IPublishedContent that returns a specific Url and doesn't go back into the UrlProvider pipeline. //TODO: We could put a try/catch here just in case, else we could do some reflection checking to see if the implementation is PublishedContentBase and the Url property is not overridden. return(UrlInfo.Url( content.Id == umbracoContext.PublishedRequest.PublishedContent.Id ? umbracoContext.PublishedRequest.PublishedContent.GetUrl(culture) : null, culture)); }
/// <inheritdoc /> public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); // get the raw source value since this is what is used by IDataEditorWithMediaPath for processing var value = prop?.GetSourceValue(culture); if (value == null) { return(null); } var propType = prop.PropertyType; string path = null; if (_propertyEditors.TryGet(propType.EditorAlias, out var editor) && editor is IDataEditorWithMediaPath dataEditor) { path = dataEditor.GetMediaPath(value); } var url = AssembleUrl(path, current, mode); return(url == null ? null : UrlInfo.Url(url.ToString(), culture)); }
/// <inheritdoc /> public virtual UrlInfo GetMediaUrl(UmbracoContext umbracoContext, IPublishedContent content, string propertyAlias, UrlMode mode, string culture, Uri current) { var prop = content.GetProperty(propertyAlias); var value = prop?.GetValue(culture); if (value == null) { return(null); } var propType = prop.PropertyType; string path = null; switch (propType.EditorAlias) { case Constants.PropertyEditors.Aliases.UploadField: path = value.ToString(); break; case Constants.PropertyEditors.Aliases.ImageCropper: //get the url from the json format path = value is ImageCropperValue stronglyTyped ? stronglyTyped.Src : value.ToString(); break; } var url = AssembleUrl(path, current, mode); return(url == null ? null : UrlInfo.Url(url.ToString(), 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; } } }
/// <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)); } }
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 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)); } } } }