public override bool TryFindContent(PublishedRequest contentRequest) { string route; if (contentRequest.HasDomain) { route = contentRequest.Domain.ContentId.ToString() + DomainUtilities.PathRelativeToDomain(contentRequest.Domain.Uri, contentRequest.Uri.GetAbsolutePathDecoded()); } else { route = contentRequest.Uri.GetAbsolutePathDecoded(); } // This simple logic should do the trick: basically if I find an url with more than 4 segments (the 3 date parts and the slug) // I leave the last segment (the slug), remove the 3 date parts, and keep all the rest. var segmentLength = contentRequest.Uri.Segments.Length; if (segmentLength > 4) { var stringDate = contentRequest.Uri.Segments[segmentLength - 4] + contentRequest.Uri.Segments[segmentLength - 3] + contentRequest.Uri.Segments[segmentLength - 2].TrimEnd("/"); DateTime postDate; try { postDate = DateTime.ParseExact(stringDate, "yyyy/MM/dd", CultureInfo.InvariantCulture); } catch (FormatException) { return(false); } var newRoute = string.Empty; for (int i = 0; i < segmentLength; i++) { if (i < segmentLength - 4 || i > segmentLength - 2) { newRoute += contentRequest.Uri.Segments[i]; } } var node = FindContent(contentRequest, newRoute); contentRequest.PublishedContent = null; // If by chance something matches the format pattern I check again if there is sucn a node and if it's an articulate post if (node == null || (node.ContentType.Alias != "ArticulateRichText" && node.ContentType.Alias != "ArticulateMarkdown")) { return(false); } if (!node.Parent.Parent.Value <bool>("useDateFormatForUrl")) { return(false); } if (node.Value <DateTime>("publishedDate").Date != postDate.Date) { return(false); } contentRequest.PublishedContent = node; return(true); } return(false); }
/// <summary> /// Gets the culture assigned to a document by domains, in the context of a current Uri. /// </summary> /// <param name="content">The document.</param> /// <param name="umbracoContextAccessor"></param> /// <param name="siteDomainHelper"></param> /// <param name="current">An optional current Uri.</param> /// <returns>The culture assigned to the document by domains.</returns> /// <remarks> /// <para> /// In 1:1 multilingual setup, a document contains several cultures (there is not /// one document per culture), and domains, withing the context of a current Uri, assign /// a culture to that document. /// </para> /// </remarks> public static string?GetCultureFromDomains( this IPublishedContent content, IUmbracoContextAccessor umbracoContextAccessor, ISiteDomainMapper siteDomainHelper, Uri?current = null) { IUmbracoContext umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); return(DomainUtilities.GetCultureFromDomains(content.Id, content.Path, current, umbracoContext, siteDomainHelper)); }
private async Task <IActionResult> GetMacroResultAsHtml(string?macroAlias, int pageId, IDictionary <string, object>?macroParams) { IMacro?m = macroAlias is null ? null : _macroService.GetByAlias(macroAlias); if (m == null) { return(NotFound()); } IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?publishedContent = umbracoContext.Content?.GetById(true, pageId); //if it isn't supposed to be rendered in the editor then return an empty string //currently we cannot render a macro if the page doesn't yet exist if (pageId == -1 || publishedContent == null || m.DontRender) { //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. return(Content(string.Empty, "text/html", Encoding.UTF8)); } // When rendering the macro in the backoffice the default setting would be to use the Culture of the logged in user. // Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want // to set the current culture to the culture related to the content item. This is hacky but it works. // fixme // in a 1:1 situation we do not handle the language being edited // so the macro renders in the wrong language var culture = DomainUtilities.GetCultureFromDomains(publishedContent.Id, publishedContent.Path, null, umbracoContext, _siteDomainHelper); if (culture != null) { Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture); } // must have an active variation context! _variationContextAccessor.VariationContext = new VariationContext(culture); using (umbracoContext.ForcedPreview(true)) { //need to create a specific content result formatted as HTML since this controller has been configured //with only json formatters. return(Content( (await _componentRenderer.RenderMacroForContent(publishedContent, m.Alias, macroParams)).ToString() ?? string.Empty, "text/html", Encoding.UTF8)); } }
public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) { if (!Utilities.DevelopmentMode) { var du = new DomainUtilities(Utilities.ResellerId, Utilities.Resellerpassword); var result = du.LogIn(model.Email, model.Password, DomainUtilities.GetPublicIp()); if (result != string.Empty) { FormsService.SignIn(model.Email, model.RememberMe); if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } else { string selectedDomains; try { selectedDomains = Session["domainnamearr"].ToString(); } catch (Exception) { selectedDomains = string.Empty; } if(selectedDomains == string.Empty) return RedirectToAction("Index", "Home"); else return Redirect("/Domain/Domain"); } /*else { ModelState.AddModelError("", "The user name or password provided is incorrect."); } */ } else { ModelState.AddModelError("Email", "The user name or password provided is incorrect."); } } else { FormsService.SignIn(model.Email, model.RememberMe); return RedirectToAction("Index", "Home"); } } // If we got this far, something failed, redisplay form return View(model); }
/// <summary> /// Gets the other URLs of a published content. /// </summary> /// <param name="umbracoContextAccessor">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(int id, Uri current) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?node = umbracoContext.Content?.GetById(id); if (node == null) { yield break; } // look for domains, walking up the tree IPublishedContent? n = node; IEnumerable <DomainAndUri>?domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, false); // n is null at root while (domainUris == null && n != null) { n = n.Parent; // move to parent node domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current); } // no domains = exit if (domainUris == null) { yield break; } foreach (DomainAndUri d in domainUris) { var culture = d.Culture; // 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.Content?.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, _requestSettings); yield return(UrlInfo.Url(uri.ToString(), culture)); } }
/// <summary> /// Initializes a new instance of the <see cref="DomainAndUri" /> class. /// </summary> /// <param name="domain">The original domain.</param> /// <param name="currentUri">The context current Uri.</param> public DomainAndUri(Domain domain, Uri currentUri) : base(domain) { try { Uri = DomainUtilities.ParseUriFromDomainName(Name, currentUri); } catch (UriFormatException) { throw new ArgumentException( $"Failed to parse invalid domain: node id={domain.ContentId}, hostname=\"{Name.ToCSharpString()}\"." + " Hostname should be a valid uri.", nameof(domain)); } }
private void AddToCacheIfDeepestRoute(IPublishedContent content, string route) { var domainRootNodeId = route.StartsWith("/") ? -1 : int.Parse(route.Substring(0, route.IndexOf('/'))); // so we have a route that maps to a content... say "1234/path/to/content" - however, there could be a // domain set on "to" and route "4567/content" would also map to the same content - and due to how // urls computing work (by walking the tree up to the first domain we find) it is that second route // that would be returned - the "deepest" route - and that is the route we want to cache, *not* the // longer one - so make sure we don't cache the wrong route var deepest = DomainUtilities.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false; if (deepest) { _routesCache.Store(content.Id, route, true); // trusted route } }
/// <summary> /// Convert a DbCustomer into a domain Customer /// </summary> public static RopResult <Customer, DomainMessage> FromDbCustomer(DbCustomer sqlCustomer) { if (sqlCustomer == null) { return(Rop.Fail <Customer, DomainMessage>(DomainMessage.CustomerNotFound())); } var firstName = DomainUtilities.CreateFirstName(sqlCustomer.FirstName); var lastName = DomainUtilities.CreateLastName(sqlCustomer.LastName); var createName = Rop.Lift2 <String10, String10, PersonalName, DomainMessage>(PersonalName.Create); var name = createName(firstName, lastName); var id = DomainUtilities.CreateCustomerId(sqlCustomer.Id); var email = DomainUtilities.CreateEmail(sqlCustomer.Email); var createCust = Rop.Lift3 <CustomerId, PersonalName, EmailAddress, Customer, DomainMessage>(Customer.Create); var cust = createCust(id, name, email); return(cust); }
/// <summary> /// Looks for wildcard domains in the path and updates <c>Culture</c> accordingly. /// </summary> internal void HandleWildcardDomains(IPublishedRequestBuilder request) { const string tracePrefix = "HandleWildcardDomains: "; if (request.PublishedContent == null) { return; } var nodePath = request.PublishedContent.Path; if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("{TracePrefix}Path={NodePath}", tracePrefix, nodePath); } var rootNodeId = request.Domain != null ? request.Domain.ContentId : (int?)null; IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); Domain? domain = DomainUtilities.FindWildcardDomainInPath(umbracoContext.PublishedSnapshot.Domains?.GetAll(true), nodePath, rootNodeId); // always has a contentId and a culture if (domain != null) { request.SetCulture(domain.Culture); if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug( "{TracePrefix}Got domain on node {DomainContentId}, set culture to {CultureName}", tracePrefix, domain.ContentId, request.Culture); } } else { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("{TracePrefix}No match.", tracePrefix); } } }
/// <summary> /// Create a domain customer from a DTO or null if not valid. /// </summary> public static RopResult <Customer, DomainMessage> DtoToCustomer(CustomerDto dto) { if (dto == null) { // dto can be null if deserialization fails return(Rop.Fail <Customer, DomainMessage>(DomainMessage.CustomerIsRequired())); } var firstName = DomainUtilities.CreateFirstName(dto.FirstName); var lastName = DomainUtilities.CreateLastName(dto.LastName); var createName = Rop.Lift2 <String10, String10, PersonalName, DomainMessage>(PersonalName.Create); var name = createName(firstName, lastName); var id = DomainUtilities.CreateCustomerId(dto.Id); var email = DomainUtilities.CreateEmail(dto.Email); var createCust = Rop.Lift3 <CustomerId, PersonalName, EmailAddress, Customer, DomainMessage>(Customer.Create); var cust = createCust(id, name, email); return(cust); }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> public virtual Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { if (!UmbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? _)) { return(Task.FromResult(false)); } string route; if (frequest.Domain != null) { route = frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded); } else { route = frequest.AbsolutePathDecoded; } IPublishedContent?node = FindContent(frequest, route); return(Task.FromResult(node != null)); }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> /// <remarks> /// Optionally, can also assign the template or anything else on the document request, although that is not /// required. /// </remarks> public async Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { if (!_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext)) { return(false); } var route = frequest.Domain != null ? frequest.Domain.ContentId + DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, frequest.AbsolutePathDecoded) : frequest.AbsolutePathDecoded; IRedirectUrl?redirectUrl = await _redirectUrlService.GetMostRecentRedirectUrlAsync(route, frequest.Culture); if (redirectUrl == null) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No match for route: {Route}", route); } return(false); } IPublishedContent?content = umbracoContext.Content?.GetById(redirectUrl.ContentId); var url = content == null ? "#" : content.Url(_publishedUrlProvider, redirectUrl.Culture); if (url.StartsWith("#")) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Route {Route} matches content {ContentId} which has no URL.", route, redirectUrl.ContentId); } return(false); } // Appending any querystring from the incoming request to the redirect URL url = string.IsNullOrEmpty(frequest.Uri.Query) ? url : url + frequest.Uri.Query; if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Route {Route} matches content {ContentId} with URL '{Url}', redirecting.", route, content?.Id, url); } frequest .SetRedirectPermanent(url) // From: http://stackoverflow.com/a/22468386/5018 // See http://issues.umbraco.org/issue/U4-8361#comment=67-30532 // Setting automatic 301 redirects to not be cached because browsers cache these very aggressively which then leads // to problems if you rename a page back to it's original name or create a new page with the original name .SetNoCacheHeader(true) .SetCacheExtensions(new List <string> { "no-store, must-revalidate" }) .SetHeaders(new Dictionary <string, string> { { "Pragma", "no-cache" }, { "Expires", "0" } }); return(true); }
public ActionResult Register(RegisterModel model) { if (ModelState.IsValid) { var du = new DomainUtilities(Utilities.ResellerId, Utilities.Resellerpassword); var result = du.SignUp(model.Email, model.Password, model.Name, model.Company, model.Address1, model.Address2, model.City, model.State, model.Country, model.Zip, model.PhoneCc, model.Mobile); if(!string.IsNullOrEmpty(result)) { FormsService.SignIn(model.Email, true/* createPersistentCookie */); string selectedDomains; try { selectedDomains = Session["domainnamearr"].ToString(); } catch (Exception) { selectedDomains = string.Empty; } if (selectedDomains == string.Empty) return RedirectToAction("Index", "Home"); return Redirect("/Domain/Domain"); } ModelState.AddModelError("Custom", "Unable to process the registration, change username and try again."); } // If we got this far, something failed, redisplay form ViewModel.PasswordLength = 6; return View(model); }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> /// <remarks>If successful, also assigns the template.</remarks> public override Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { var path = frequest.AbsolutePathDecoded; if (frequest.Domain != null) { path = DomainUtilities.PathRelativeToDomain(frequest.Domain.Uri, path); } // no template if "/" if (path == "/") { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("No template in path '/'"); } return(Task.FromResult(false)); } // look for template in last position var pos = path.LastIndexOf('/'); var templateAlias = path.Substring(pos + 1); path = pos == 0 ? "/" : path.Substring(0, pos);; ITemplate?template = _fileService.GetTemplate(templateAlias); if (template == null) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Not a valid template: '{TemplateAlias}'", templateAlias); } return(Task.FromResult(false)); } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Valid template: '{TemplateAlias}'", templateAlias); } // look for node corresponding to the rest of the route var route = frequest.Domain != null ? frequest.Domain.ContentId + path : path; IPublishedContent?node = FindContent(frequest, route); if (node == null) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Not a valid route to node: '{Route}'", route); } return(Task.FromResult(false)); } // IsAllowedTemplate deals both with DisableAlternativeTemplates and ValidateAlternativeTemplates settings if (!node.IsAllowedTemplate(_contentTypeService, _webRoutingSettings, template.Id)) { _logger.LogWarning( "Alternative template '{TemplateAlias}' is not allowed on node {NodeId}.", template.Alias, node.Id); frequest.SetPublishedContent(null); // clear return(Task.FromResult(false)); } // got it frequest.SetTemplate(template); return(Task.FromResult(true)); }
public ActionResult DomainChecking(string domainName, string domains) { var notavailablelist = new List<string>(); var availablelist = new List<Domaindetails>(); if (!Utilities.DevelopmentMode) { if (!string.IsNullOrEmpty(domainName) && !string.IsNullOrEmpty(domains)) { var du = new DomainUtilities(Utilities.ResellerId, Utilities.Resellerpassword); var dms = domains.Split(',').ToList(); string json = du.Checkdomainavailability(domainName, dms); if (!string.IsNullOrEmpty(json)) { var ser = new JavaScriptSerializer(); foreach (var domain in (Dictionary<String, object>)ser.Deserialize<Object>(json)) { foreach (var details in (Dictionary<String, object>)domain.Value) { if (details.Value.ToString().ToLower() == "available") availablelist.Add(new Domaindetails { Extension = domain.Key.Substring(domain.Key.IndexOf('.') + 1), Name = domain.Key.Substring(0, domain.Key.IndexOf('.')) }); else notavailablelist.Add(domain.Key); break; } } } ViewData["NotAvailableList"] = notavailablelist; ViewData["AvailableList"] = availablelist; return View(); } } else { availablelist.Add(new Domaindetails { Extension = "com", Name = "santhoshinet" }); availablelist.Add(new Domaindetails { Extension = "in", Name = "santhoshinet" }); ViewData["NotAvailableList"] = notavailablelist; ViewData["AvailableList"] = availablelist; return View(); } Response.Redirect("/index.htm"); return null; }
/// <summary> /// Finds the site root (if any) matching the http request, and updates the PublishedRequest accordingly. /// </summary> /// <returns>A value indicating whether a domain was found.</returns> internal bool FindDomain(IPublishedRequestBuilder request) { const string tracePrefix = "FindDomain: "; // note - we are not handling schemes nor ports here. if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("{TracePrefix}Uri={RequestUri}", tracePrefix, request.Uri); } IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IDomainCache? domainsCache = umbracoContext.PublishedSnapshot.Domains; var domains = domainsCache?.GetAll(false).ToList(); // determines whether a domain corresponds to a published document, since some // domains may exist but on a document that has been unpublished - as a whole - or // that is not published for the domain's culture - in which case the domain does // not apply bool IsPublishedContentDomain(Domain domain) { // just get it from content cache - optimize there, not here IPublishedContent?domainDocument = umbracoContext.PublishedSnapshot.Content?.GetById(domain.ContentId); // not published - at all if (domainDocument == null) { return(false); } // invariant - always published if (!domainDocument.ContentType.VariesByCulture()) { return(true); } // variant, ensure that the culture corresponding to the domain's language is published return(domain.Culture is not null && domainDocument.Cultures.ContainsKey(domain.Culture)); } domains = domains?.Where(IsPublishedContentDomain).ToList(); var defaultCulture = domainsCache?.DefaultCulture; // try to find a domain matching the current request DomainAndUri?domainAndUri = DomainUtilities.SelectDomain(domains, request.Uri, defaultCulture: defaultCulture); // handle domain - always has a contentId and a culture if (domainAndUri != null) { // matching an existing domain if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug( "{TracePrefix}Matches domain={Domain}, rootId={RootContentId}, culture={Culture}", tracePrefix, domainAndUri.Name, domainAndUri.ContentId, domainAndUri.Culture); } request.SetDomain(domainAndUri); // canonical? not implemented at the moment // if (...) // { // _pcr.RedirectUrl = "..."; // return true; // } } else { // not matching any existing domain if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("{TracePrefix}Matches no domain", tracePrefix); } request.SetCulture(defaultCulture ?? CultureInfo.CurrentUICulture.Name); } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("{TracePrefix}Culture={CultureName}", tracePrefix, request.Culture); } return(request.Domain != null); }
/// <summary> /// Tries to find and assign an Umbraco document to a <c>PublishedRequest</c>. /// </summary> /// <param name="frequest">The <c>PublishedRequest</c>.</param> /// <returns>A value indicating whether an Umbraco document was found and assigned.</returns> public Task <bool> TryFindContent(IPublishedRequestBuilder frequest) { if (!_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext)) { return(Task.FromResult(false)); } if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Looking for a page to handle 404."); } int?domainContentId = null; // try to find a culture as best as we can var errorCulture = CultureInfo.CurrentUICulture.Name; if (frequest.Domain != null) { errorCulture = frequest.Domain.Culture; domainContentId = frequest.Domain.ContentId; } else { var route = frequest.AbsolutePathDecoded; var pos = route.LastIndexOf('/'); IPublishedContent?node = null; while (pos > 1) { route = route.Substring(0, pos); node = umbracoContext.Content?.GetByRoute(route, culture: frequest?.Culture); if (node != null) { break; } pos = route.LastIndexOf('/'); } if (node != null) { Domain?d = DomainUtilities.FindWildcardDomainInPath( umbracoContext.PublishedSnapshot.Domains?.GetAll(true), node.Path, null); if (d != null) { errorCulture = d.Culture; } } } var error404 = NotFoundHandlerHelper.GetCurrentNotFoundPageId( _contentSettings.Error404Collection.ToArray(), _entityService, new PublishedContentQuery(umbracoContext.PublishedSnapshot, _variationContextAccessor, _examineManager), errorCulture, domainContentId); IPublishedContent?content = null; if (error404.HasValue) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Got id={ErrorNodeId}.", error404.Value); } content = umbracoContext.Content?.GetById(error404.Value); if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(content == null ? "Could not find content with that id." : "Found corresponding content."); } } else { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug("Got nothing."); } } frequest? .SetPublishedContent(content) .SetIs404(); return(Task.FromResult(content != null)); }
null; // we have nothing to say #endregion #region GetOtherUrls /// <summary> /// Gets the other URLs of a published content. /// </summary> /// <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(int id, Uri current) { IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); IPublishedContent?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 IPublishedContent? n = node; IEnumerable <DomainAndUri>?domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, false); // n is null at root while (domainUris == null && n != null) { // move to parent node n = n.Parent; domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, 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>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(Constants.CharArrays.Comma, 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, _requestConfig).ToString())); } } else { // some domains: one URL per domain, which is "<domain>/<alias>" foreach (DomainAndUri 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)) { continue; } var umbracoUrlName = varies ? node.Value <string>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias, domainUri.Culture) : node.Value <string>(_publishedValueFallback, Constants.Conventions.Content.UrlAlias); var aliases = umbracoUrlName?.Split(Constants.CharArrays.Comma, 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, _requestConfig).ToString(), domainUri.Culture)); } } } }