Fixes more bugs and outbound routing for additional urls

This commit is contained in:
Shannon
2018-04-26 22:54:36 +10:00
parent 23c40cbf2c
commit ffd85b4e94
9 changed files with 75 additions and 296 deletions

View File

@@ -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<ISiteDomainHelper, SiteDomainHelper>();
}
[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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -232,7 +232,6 @@
<Compile Include="Strings\StylesheetHelperTests.cs" />
<Compile Include="Strings\StringValidationTests.cs" />
<Compile Include="FrontEnd\UmbracoHelperTests.cs" />
<Compile Include="Integration\GetCultureTests.cs" />
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />
<Compile Include="Migrations\Stubs\FiveZeroMigration.cs" />

View File

@@ -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
///// <summary>
///// Gets the culture that would be selected to render a specified content,
///// within the context of a specified current request.
///// </summary>
///// <param name="content">The content.</param>
///// <param name="current">The request Uri.</param>
///// <returns>The culture that would be selected to render the content.</returns>
//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
/// <summary>
/// Gets the culture that would be selected to render a specified content,
/// within the context of a specified current request.
/// </summary>
/// <param name="umbracoContext">An <see cref="UmbracoContext"/> instance.</param>
/// <param name="domainService">An <see cref="IDomainService"/> implementation.</param>
/// <param name="localizationService">An <see cref="ILocalizationService"/> implementation.</param>
/// <param name="contentService">An <see cref="IContentService"/> implementation.</param>
/// <param name="contentId">The content identifier.</param>
/// <param name="contentPath">The content path.</param>
/// <param name="current">The request Uri.</param>
/// <returns>The culture that would be selected to render the content.</returns>
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);
}
}
}

View File

@@ -170,7 +170,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
if (_cultureNames == null)
{
var d = new Dictionary<string, PublishedCultureName>();
var d = new Dictionary<string, PublishedCultureName>(StringComparer.InvariantCultureIgnoreCase);
foreach(var c in _contentData.CultureInfos)
{
d[c.Key] = new PublishedCultureName(c.Value.Name, c.Value.Name.ToUrlSegment());

View File

@@ -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
{
/// <summary>
@@ -90,32 +92,79 @@ namespace Umbraco.Web.Routing
/// urls for the node in other contexts (different domain for current request, umbracoUrlAlias...).</para>
/// </remarks>
public virtual IEnumerable<string> 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<DefaultUrlProvider>(() =>
$"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<string>();
}
var result = new List<string>();
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 /<path> or <domainRootId>/<path>
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<string> GetOtherUrlsForSinglePath(string route, int id, DomainHelper domainHelper, Uri current)
{
if (string.IsNullOrWhiteSpace(route))
{
_logger.Debug<DefaultUrlProvider>(() =>
$"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 /<path> or <domainRootId>/<path>
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)
{

View File

@@ -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
{

View File

@@ -567,7 +567,6 @@
<Compile Include="Models\ContentEditing\Relation.cs" />
<Compile Include="HtmlStringUtilities.cs" />
<Compile Include="Models\ContentEditing\RelationType.cs" />
<Compile Include="Models\ContentExtensions.cs" />
<Compile Include="ITagQuery.cs" />
<Compile Include="IUmbracoComponentRenderer.cs" />
<Compile Include="Models\Mapping\RelationMapperProfile.cs" />

View File

@@ -481,7 +481,7 @@ namespace umbraco
if (_cultureNames == null)
{
var d = new Dictionary<string, PublishedCultureName>();
var d = new Dictionary<string, PublishedCultureName>(StringComparer.InvariantCultureIgnoreCase);
foreach (var c in _inner.Names)
{
d[c.Key] = new PublishedCultureName(c.Value, c.Value.ToUrlSegment());