diff --git a/src/Umbraco.Core/ContentVariationExtensions.cs b/src/Umbraco.Core/ContentVariationExtensions.cs index 092de4d6d6..d18fb4b091 100644 --- a/src/Umbraco.Core/ContentVariationExtensions.cs +++ b/src/Umbraco.Core/ContentVariationExtensions.cs @@ -75,6 +75,26 @@ namespace Umbraco.Core /// public static bool VariesByCultureAndSegment(this PublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment(); + /// + /// Determines whether the property type is invariant. + /// + public static bool VariesByNothing(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing(); + + /// + /// Determines whether the property type varies by culture. + /// + public static bool VariesByCulture(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture(); + + /// + /// Determines whether the property type varies by segment. + /// + public static bool VariesBySegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment(); + + /// + /// Determines whether the property type varies by culture and segment. + /// + public static bool VariesByCultureAndSegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment(); + /// /// Determines whether a variation is invariant. /// diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index d33372ce53..5350e05ef9 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -229,6 +229,17 @@ namespace Umbraco.Web #endregion + #region Variations + + /// + /// Determines whether the content has a culture. + /// + /// Culture is case-insensitive. + public static bool HasCulture(this IPublishedContent content, string culture) + => content.Cultures.ContainsKey(culture); + + #endregion + #region Search public static IEnumerable Search(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) diff --git a/src/Umbraco.Web/Routing/AliasUrlProvider.cs b/src/Umbraco.Web/Routing/AliasUrlProvider.cs index 900c41e1ff..8c851d139f 100644 --- a/src/Umbraco.Web/Routing/AliasUrlProvider.cs +++ b/src/Umbraco.Web/Routing/AliasUrlProvider.cs @@ -54,25 +54,36 @@ namespace Umbraco.Web.Routing public IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { var node = umbracoContext.ContentCache.GetById(id); - if (node == null) - return Enumerable.Empty(); + if (node == null) return Enumerable.Empty(); if (!node.HasProperty(Constants.Conventions.Content.UrlAlias)) return Enumerable.Empty(); 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 { // move to parent node n = n.Parent; - domainUris = n == null ? null : domainHelper.DomainsForNode(n.Id, current, false); + domainUris = n == null ? null : domainHelper.DomainsForNode(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 "/" 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) + return Enumerable.Empty(); + var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias); if (string.IsNullOrWhiteSpace(umbracoUrlName)) return Enumerable.Empty(); @@ -83,10 +94,20 @@ namespace Umbraco.Web.Routing } else { + // some domains: one url per domain, which is "/" var result = new List(); foreach(var domainUri in domainUris) { - var umbracoUrlName = node.Value(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name); + // if the property is invariant, get the invariant value, url is "/" + // if the property varies, get the variant value, url is "/" + + // but! only if the culture is published, else ignore + if (varies && !node.HasCulture(domainUri.Culture.Name)) continue; + + var umbracoUrlName = varies + ? node.Value(Constants.Conventions.Content.UrlAlias, culture: domainUri.Culture.Name) + : node.Value(Constants.Conventions.Content.UrlAlias); + if (!string.IsNullOrWhiteSpace(umbracoUrlName)) { var path = "/" + umbracoUrlName; diff --git a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs index 11de879bc8..59e30cc8b0 100644 --- a/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs +++ b/src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs @@ -38,6 +38,7 @@ namespace Umbraco.Web.Routing { node = FindContentByAlias(frequest.UmbracoContext.ContentCache, frequest.HasDomain ? frequest.Domain.ContentId : 0, + frequest.Culture.Name, frequest.Uri.GetAbsolutePathDecoded()); if (node != null) @@ -50,7 +51,7 @@ namespace Umbraco.Web.Routing return node != null; } - private static IPublishedContent FindContentByAlias(IPublishedContentCache cache, int rootNodeId, string alias) + private static IPublishedContent FindContentByAlias(IPublishedContentCache cache, int rootNodeId, string culture, string alias) { if (alias == null) throw new ArgumentNullException(nameof(alias)); @@ -62,7 +63,7 @@ namespace Umbraco.Web.Routing // can we normalize the values so that they contain no whitespaces, and no leading slashes? // and then the comparisons in IsMatch can be way faster - and allocate way less strings - const string propertyAlias = "umbracoUrlAlias"; + const string propertyAlias = Constants.Conventions.Content.UrlAlias; var test1 = alias.TrimStart('/') + ","; var test2 = ",/" + test1; // test2 is ",/alias," @@ -78,12 +79,26 @@ namespace Umbraco.Web.Routing // ")]" if (!c.HasProperty(propertyAlias)) return false; - var v = c.Value(propertyAlias); + var p = c.GetProperty(propertyAlias); + var varies = p.PropertyType.VariesByCulture(); + string v; + if (varies) + { + if (!c.HasCulture(culture)) return false; + v = c.Value(propertyAlias, culture); + } + else + { + v = c.Value(propertyAlias); + } if (string.IsNullOrWhiteSpace(v)) return false; v = "," + v.Replace(" ", "") + ","; return v.Contains(a1) || v.Contains(a2); } + // fixme - even with Linq, what happens below has to be horribly slow + // but the only solution is to entirely refactor url providers to stop being dynamic + if (rootNodeId > 0) { var rootNode = cache.GetById(rootNodeId); diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index 9e89459774..fb9cdfa9bd 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -80,8 +80,11 @@ namespace Umbraco.Web.Routing public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) { var node = umbracoContext.ContentCache.GetById(id); + if (node == null) return Enumerable.Empty(); + 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 diff --git a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs index ce09bdc645..b8671b5735 100644 --- a/src/Umbraco.Web/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Web/Routing/UrlProviderExtensions.cs @@ -110,11 +110,14 @@ namespace Umbraco.Web.Routing foreach (var (text, infos) in dmsg) ret.Add(UrlInfo.Message(text, infos.Count == cultures.Count ? null : string.Join(", ", infos.Select(x => x.Culture)))); - // fixme - need to add 'others' urls - // but, when? - //// get the 'other' urls - //foreach(var otherUrl in urlProvider.GetOtherUrls(content.Id)) - // urls.Add(otherUrl); + // 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. + // also, we are not dealing with cultures at all - that will have to wait + foreach(var otherUrl in umbracoContext.UrlProvider.GetOtherUrls(content.Id)) + { + if (urls.Any(x => x.IsUrl && x.Text == otherUrl)) continue; + ret.Add(UrlInfo.Url(otherUrl)); + } return ret; }