diff --git a/src/Umbraco.Tests/Integration/GetCultureTests.cs b/src/Umbraco.Tests/Integration/GetCultureTests.cs deleted file mode 100644 index ec3a73b37e..0000000000 --- a/src/Umbraco.Tests/Integration/GetCultureTests.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using NUnit.Framework; -using Umbraco.Core.Models; -using Umbraco.Tests.Services; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.Testing; -using Umbraco.Web.Routing; - -namespace Umbraco.Tests.Integration -{ - [TestFixture] - [Apartment(ApartmentState.STA)] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class GetCultureTests : TestWithSomeContentBase - { - protected override void Compose() - { - base.Compose(); - Container.Register(); - } - - [Test] - public void GetCulture() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.com") {DomainName="domain1.com", RootContentId = c1.Id, LanguageId = l0.Id}); - ServiceContext.DomainService.Save(new UmbracoDomain("domain1.fr") { DomainName = "domain1.fr", RootContentId = c1.Id, LanguageId = l1.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("en-US", culture.Name); - - content = c2; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - - content = c4; - culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("de-DE", culture.Name); - } - - [Test] - public void GetCultureWithWildcard() - { - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType("umbBlah", "test Doc Type"); - contentTypeService.Save(contentType); - var contentService = ServiceContext.ContentService; - - var c1 = contentService.CreateAndSave("content", -1, "umbBlah"); - var c2 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c3 = contentService.CreateAndSave("content", c1, "umbBlah"); - var c4 = contentService.CreateAndSave("content", c3, "umbBlah"); - - foreach (var l in ServiceContext.LocalizationService.GetAllLanguages().Where(x => x.CultureName != "en-US").ToArray()) - ServiceContext.LocalizationService.Delete(l); - - var l0 = ServiceContext.LocalizationService.GetLanguageByIsoCode("en-US"); - var l1 = new Core.Models.Language("fr-FR"); - var l2 = new Core.Models.Language("de-DE"); - ServiceContext.LocalizationService.Save(l1); - ServiceContext.LocalizationService.Save(l2); - - foreach (var d in ServiceContext.DomainService.GetAll(true).ToArray()) - ServiceContext.DomainService.Delete(d); - - ServiceContext.DomainService.Save(new UmbracoDomain("*0000") { DomainName = "*0000", RootContentId = c1.Id, LanguageId = l2.Id }); - ServiceContext.DomainService.Save(new UmbracoDomain("*0001") { DomainName = "*0001", RootContentId = c3.Id, LanguageId = l1.Id }); - //ServiceContext.DomainService.Save(new UmbracoDomain("*100112") { DomainName = "*100112", RootContentId = c3.Id, LanguageId = l2.Id }); - - var content = c2; - var culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.com/")); - Assert.AreEqual("de-DE", culture.Name); - - content = c4; - culture = Umbraco.Web.Models.ContentExtensions.GetCulture(null, - ServiceContext.DomainService, ServiceContext.LocalizationService, ServiceContext.ContentService, - new SiteDomainHelper(), - content.Id, content.Path, new Uri("http://domain1.fr/")); - Assert.AreEqual("fr-FR", culture.Name); - } - } -} diff --git a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs index 1740f1288c..e4fcfc46f8 100644 --- a/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs @@ -338,48 +338,6 @@ namespace Umbraco.Tests.Routing } - - #region Cases - [TestCase(10011, "http://domain1.com/", "en-US")] - [TestCase(100111, "http://domain1.com/", "en-US")] - [TestCase(10011, "http://domain1.fr/", "fr-FR")] - [TestCase(100111, "http://domain1.fr/", "fr-FR")] - [TestCase(1001121, "http://domain1.fr/", "de-DE")] - #endregion - public void GetCulture(int nodeId, string currentUrl, string expectedCulture) - { - var domainService = SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 1001, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("domain1.fr/") - { - Id = 1, - LanguageId = LangFrId, - RootContentId = 1001, - LanguageIsoCode = "fr-FR" - }, - new UmbracoDomain("*100112") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 100112, - LanguageIsoCode = "de-DE" - } - }); - - var umbracoContext = GetUmbracoContext("http://anything/"); - - var content = umbracoContext.ContentCache.GetById(nodeId); - Assert.IsNotNull(content); - - var culture = global::Umbraco.Web.Models.ContentExtensions.GetCulture(umbracoContext, domainService, ServiceContext.LocalizationService, null, new SiteDomainHelper(), content.Id, content.Path, new Uri(currentUrl)); - Assert.AreEqual(expectedCulture, culture.Name); - } + } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3da04b48ca..8ae1e4dede 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -232,7 +232,6 @@ - diff --git a/src/Umbraco.Web/Models/ContentExtensions.cs b/src/Umbraco.Web/Models/ContentExtensions.cs deleted file mode 100644 index d6700769a9..0000000000 --- a/src/Umbraco.Web/Models/ContentExtensions.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using Umbraco.Web.Composing; -using Umbraco.Web.Routing; -using Domain = Umbraco.Web.Routing.Domain; - -namespace Umbraco.Web.Models -{ - public static class ContentExtensions - { - //TODO: Not used - ///// - ///// Gets the culture that would be selected to render a specified content, - ///// within the context of a specified current request. - ///// - ///// The content. - ///// The request Uri. - ///// The culture that would be selected to render the content. - //public static CultureInfo GetCulture(this IContent content, Uri current = null) - //{ - // return GetCulture(UmbracoContext.Current, - // Current.Services.DomainService, - // Current.Services.LocalizationService, - // Current.Services.ContentService, - // content.Id, content.Path, - // current); - //} - - - - //TODO: Not used - only in tests - /// - /// Gets the culture that would be selected to render a specified content, - /// within the context of a specified current request. - /// - /// An instance. - /// An implementation. - /// An implementation. - /// An implementation. - /// The content identifier. - /// The content path. - /// The request Uri. - /// The culture that would be selected to render the content. - internal static CultureInfo GetCulture(UmbracoContext umbracoContext, - IDomainService domainService, ILocalizationService localizationService, IContentService contentService, - ISiteDomainHelper siteDomainHelper, - int contentId, string contentPath, Uri current) - { - var route = umbracoContext == null - ? null // for tests only - : umbracoContext.ContentCache.GetRouteById(contentId); // may be cached - - var domainCache = umbracoContext == null - ? new PublishedCache.XmlPublishedCache.DomainCache(domainService, localizationService) // for tests only - : umbracoContext.PublishedShapshot.Domains; // default - var domainHelper = umbracoContext.GetDomainHelper(siteDomainHelper); - Domain domain; - - if (route == null) - { - // if content is not published then route is null and we have to work - // on non-published content (note: could optimize by checking routes?) - - // fixme - even non-published content is stored in the cache or in the cmsContentNu table which would be faster to lookup - - var content = contentService.GetById(contentId); - if (content == null) - return GetDefaultCulture(localizationService); - - var hasDomain = domainHelper.NodeHasDomains(content.Id); - while (hasDomain == false && content != null) - { - content = content.Parent(contentService); - hasDomain = content != null && domainHelper.NodeHasDomains(content.Id); - } - - domain = hasDomain ? domainHelper.DomainForNode(content.Id, current) : null; - } - else - { - // if content is published then we have a route - // from which we can figure out the domain - - var pos = route.IndexOf('/'); - domain = pos == 0 - ? null - : domainHelper.DomainForNode(int.Parse(route.Substring(0, pos)), current); - } - - var rootContentId = domain == null ? -1 : domain.ContentId; - var wcDomain = DomainHelper.FindWildcardDomainInPath(domainCache.GetAll(true), contentPath, rootContentId); - - if (wcDomain != null) return wcDomain.Culture; - if (domain != null) return domain.Culture; - return GetDefaultCulture(localizationService); - } - - private static CultureInfo GetDefaultCulture(ILocalizationService localizationService) - { - var defaultLanguage = localizationService.GetDefaultVariantLanguage(); - return defaultLanguage == null ? CultureInfo.CurrentUICulture : new CultureInfo(defaultLanguage.IsoCode); - } - } -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs index 883a43a7d5..480c18201f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedContent.cs @@ -170,7 +170,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (_cultureNames == null) { - var d = new Dictionary(); + var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach(var c in _contentData.CultureInfos) { d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment()); diff --git a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs index d2f0c2662b..21a4b5c734 100644 --- a/src/Umbraco.Web/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Web/Routing/DefaultUrlProvider.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; - +using Umbraco.Core.Models; + namespace Umbraco.Web.Routing { /// @@ -90,32 +92,79 @@ namespace Umbraco.Web.Routing /// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...). /// public virtual IEnumerable GetOtherUrls(UmbracoContext umbracoContext, int id, Uri current) - { - // will not use cache if previewing - var route = umbracoContext.ContentCache.GetRouteById(id); - - if (string.IsNullOrWhiteSpace(route)) - { - _logger.Debug(() => - $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); - return null; + { + var node = umbracoContext.ContentCache.GetById(id); + var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); + + //if this is invariant, continue as we used to + if (!node.ContentType.Variations.Has(ContentVariation.CultureNeutral)) + { + // will not use cache if previewing + var route = umbracoContext.ContentCache.GetRouteById(id); + + return GetOtherUrlsForSinglePath(route, id, domainHelper, current); + } + else + { + //this is variant, so we need to find the domains and use the cultures assigned to get the route/paths + + 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); + } + + if (domainUris == null) + { + //we can't continue, there are no domains assigned but this is a culture variant node + return Enumerable.Empty(); + } + + var result = new List(); + foreach(var d in domainUris) + { + var route = umbracoContext.ContentCache.GetRouteById(id, d.Culture); + if (route == null) continue; + + //need to strip off the leading ID for the route + //TODO: Is there a nicer way to deal with this? + 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); + result.Add(uri.ToString()); + } + return result; } - - var domainHelper = umbracoContext.GetDomainHelper(_siteDomainHelper); - - // extract domainUri and path - // route is / or / - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); - - // assemble the alternate urls from domainUris (maybe empty) and path - return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); + } #endregion #region Utilities + + private IEnumerable GetOtherUrlsForSinglePath(string route, int id, DomainHelper domainHelper, Uri current) + { + if (string.IsNullOrWhiteSpace(route)) + { + _logger.Debug(() => + $"Couldn't find any page with nodeId={id}. This is most likely caused by the page not being published."); + return null; + } + + // extract domainUri and path + // route is / or / + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var domainUris = pos == 0 ? null : domainHelper.DomainsForNode(int.Parse(route.Substring(0, pos)), current); + + // assemble the alternate urls from domainUris (maybe empty) and path + return AssembleUrls(domainUris, path).Select(uri => uri.ToString()); + } Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlProviderMode mode) { diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs index d56473f530..ba7b59323b 100644 --- a/src/Umbraco.Web/Routing/DomainHelper.cs +++ b/src/Umbraco.Web/Routing/DomainHelper.cs @@ -128,7 +128,8 @@ namespace Umbraco.Web.Routing { //get the default domain (there should be one) domainAndUri = domainsAndUris.FirstOrDefault(x => x.IsDefault); - if (domainAndUri == null) domainsAndUris.First(); // take the first one by default (what else can we do?) + if (domainAndUri == null) + domainAndUri = domainsAndUris.First(); // take the first one by default (what else can we do?) } else { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 98afb387d4..44bc3ec324 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -567,7 +567,6 @@ - diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs index 274ea83214..89ac52949f 100644 --- a/src/Umbraco.Web/umbraco.presentation/page.cs +++ b/src/Umbraco.Web/umbraco.presentation/page.cs @@ -481,7 +481,7 @@ namespace umbraco if (_cultureNames == null) { - var d = new Dictionary(); + var d = new Dictionary(StringComparer.InvariantCultureIgnoreCase); foreach (var c in _inner.Names) { d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment());