From bb7752c48340f080d692cb230ca1e16a2654f05a Mon Sep 17 00:00:00 2001 From: Lewis Hazell Date: Fri, 31 Mar 2023 19:36:25 +0100 Subject: [PATCH] Case-insensitive tag names with SQLite provider (#14012) * Ensure case insensitivity in tag name queries. Use LIKE instead of = to ignore collation rules. * Add integration test for creating tag relations with mixed casing. (cherry picked from commit 37d737f9bc61a19fd65ec34d46c80699618cb384) --- .../Repositories/Implement/TagRepository.cs | 4 +- .../Repositories/TagRepositoryTest.cs | 40 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs index ecc6600d4c..bc78596762 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TagRepository.cs @@ -131,7 +131,7 @@ internal class TagRepository : EntityRepositoryBase, ITagRepository var sql1 = $@"INSERT INTO cmsTags (tag, {group}, languageId) SELECT tagSet.tag, tagSet.{group}, tagSet.languageId FROM {tagSetSql} -LEFT OUTER JOIN cmsTags ON (tagSet.tag = cmsTags.tag AND tagSet.{group} = cmsTags.{group} AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1)) +LEFT OUTER JOIN cmsTags ON (tagSet.tag LIKE cmsTags.tag AND tagSet.{group} = cmsTags.{group} AND COALESCE(tagSet.languageId, -1) = COALESCE(cmsTags.languageId, -1)) WHERE cmsTags.id IS NULL"; Database.Execute(sql1); @@ -321,7 +321,7 @@ WHERE r.tagId IS NULL"; Sql sql = GetTaggedEntitiesSql(objectType, culture); sql = sql - .Where(dto => dto.Text == tag); + .WhereLike(dto => dto.Text, tag); if (group.IsNullOrWhiteSpace() == false) { diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TagRepositoryTest.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TagRepositoryTest.cs index 1ce5eeefd9..a92123e934 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TagRepositoryTest.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TagRepositoryTest.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Infrastructure.Scoping; using Umbraco.Cms.Tests.Common.Builders; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; +using static Umbraco.Cms.Core.Constants.Conventions; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repositories; @@ -98,6 +99,45 @@ public class TagRepositoryTest : UmbracoIntegrationTest } } + [Test] + public void Can_Create_Tag_Relations_With_Mixed_Casing() + { + var provider = ScopeProvider; + using (ScopeProvider.CreateScope()) + { + var template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); + + var contentType = + ContentTypeBuilder.CreateSimpleContentType("test", "Test", defaultTemplateId: template.Id); + ContentTypeRepository.Save(contentType); + + var content1 = ContentBuilder.CreateSimpleContent(contentType); + var content2 = ContentBuilder.CreateSimpleContent(contentType); + DocumentRepository.Save(content1); + DocumentRepository.Save(content2); + + var repository = CreateRepository(provider); + Tag[] tags1 = { new Tag { Text = "tag1", Group = "test" } }; + repository.Assign( + content1.Id, + contentType.PropertyTypes.First().Id, + tags1, + false); + + // Note the casing is different from tags1, but both should be considered equivalent + Tag[] tags2 = { new Tag { Text = "TAG1", Group = "test" } }; + repository.Assign( + content2.Id, + contentType.PropertyTypes.First().Id, + tags2, + false); + + // The template should have only one tag, despite case differences + Assert.AreEqual(1, repository.GetTaggedEntitiesByTag(TaggableObjectTypes.Content, "tag1").Count()); + } + } + [Test] public void Can_Append_Tag_Relations() {