From 4290f3b93261b6a8fd8b9b0d54dbae58a9af3a0a Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 1 Feb 2021 15:44:57 +0300 Subject: [PATCH] CmsKit - Add UrlSlug to Blog --- .../Volo/CmsKit/Admin/Blogs/BlogDto.cs | 6 + .../Volo/CmsKit/Blogs/BlogConsts.cs | 1 + .../Volo.CmsKit.Domain.csproj | 4 + .../Volo/CmsKit/Blogs/Blog.cs | 12 ++ .../Volo/CmsKit/Blogs/BlogPost.cs | 25 +--- .../Blogs/Extensions/UrlSlugExtensions.cs | 30 ++++ .../Blogs/UrlSlugExtensions_Tests.cs | 139 ++++++++++++++++++ .../CmsKitDataSeedContributor.cs | 2 +- .../Volo.CmsKit.TestBase/CmsKitTestData.cs | 2 + 9 files changed, 197 insertions(+), 24 deletions(-) create mode 100644 modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/UrlSlugExtensions.cs create mode 100644 modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/UrlSlugExtensions_Tests.cs diff --git a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs index 1b772c51e5..0f112d15b8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Admin.Application.Contracts/Volo/CmsKit/Admin/Blogs/BlogDto.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Application.Dtos; using Volo.Abp.Validation; using Volo.CmsKit.Blogs; @@ -7,7 +8,12 @@ namespace Volo.CmsKit.Admin.Blogs { public class BlogDto : EntityDto { + [Required] [DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] public string Name { get; set; } + + [Required] + [DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxUrlSlugLength))] + public string UrlSlug { get; set; } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs index e722d4656d..7d2467226d 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogConsts.cs @@ -3,5 +3,6 @@ public class BlogConsts { public static int MaxNameLength { get; set; } = 64; + public static int MaxUrlSlugLength { get; set; } = 64; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj index 0fc25899e9..7355bc87d1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo.CmsKit.Domain.csproj @@ -8,6 +8,10 @@ + + + + diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs index 0ec587f52e..1dc571d69f 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs @@ -3,6 +3,7 @@ using System; using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; +using Volo.CmsKit.Blogs.Extensions; namespace Volo.CmsKit.Blogs { @@ -11,19 +12,30 @@ namespace Volo.CmsKit.Blogs public Blog( Guid id, [NotNull] string name, + [NotNull] string urlSlug, [CanBeNull] Guid? tenantId = null) : base(id) { SetName(name); + SetUrlSlug(urlSlug); TenantId = tenantId; } public string Name { get; protected set; } + public string UrlSlug { get; protected set; } + public Guid? TenantId { get; } public void SetName(string name) { Name = Check.NotNullOrWhiteSpace(name, nameof(name), maxLength: BlogConsts.MaxNameLength); } + + public void SetUrlSlug(string urlSlug) + { + Check.NotNullOrWhiteSpace(urlSlug, nameof(urlSlug), maxLength: BlogConsts.MaxNameLength); + + UrlSlug = urlSlug.NormalizeAsUrlSlug(); + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs index 62a73ec352..db0d22b18a 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPost.cs @@ -5,6 +5,7 @@ using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; using Volo.CmsKit.Blogs; +using Volo.CmsKit.Blogs.Extensions; using Volo.CmsKit.Users; namespace Volo.CmsKit.Blogs @@ -47,29 +48,7 @@ namespace Volo.CmsKit.Blogs { Check.NotNullOrWhiteSpace(urlSlug, nameof(urlSlug), BlogPostConsts.MaxUrlSlugLength, BlogPostConsts.MinUrlSlugLength); - UrlSlug = NormalizeUrlSlug(urlSlug); - } - - private string NormalizeUrlSlug(string value) - { - value = value.ToLowerInvariant(); - - // TODO: Find best way to unidecode. - // value = value.Unidecode(); - - // Replace spaces - value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled); - - // Remove invalid chars - value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled); - - // Trim dashes from end & dots - value = value.Trim('-', '_', '.'); - - // Replace double occurences of - or _ - value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled); - - return value; + UrlSlug = urlSlug.NormalizeAsUrlSlug(); } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/UrlSlugExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/UrlSlugExtensions.cs new file mode 100644 index 0000000000..45765c4a09 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Extensions/UrlSlugExtensions.cs @@ -0,0 +1,30 @@ +using System.Text.RegularExpressions; +using Unidecode.NET; + +namespace Volo.CmsKit.Blogs.Extensions +{ + public static class UrlSlugExtensions + { + public static string NormalizeAsUrlSlug(this string value) + { + value = value.ToLowerInvariant(); + + // Unidecode for non-latin characters + value = value.Unidecode(); + + // Replace spaces + value = Regex.Replace(value, @"\s", "-", RegexOptions.Compiled); + + // Remove invalid chars + value = Regex.Replace(value, @"[^a-z0-9\s-_]", "", RegexOptions.Compiled); + + // Trim dashes & dots + value = value.Trim('-', '_', '.'); + + // Replace double occurences of - or _ + value = Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled); + + return value; + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/UrlSlugExtensions_Tests.cs b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/UrlSlugExtensions_Tests.cs new file mode 100644 index 0000000000..0c32ab3285 --- /dev/null +++ b/modules/cms-kit/test/Volo.CmsKit.Domain.Tests/Blogs/UrlSlugExtensions_Tests.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; +using Volo.CmsKit.Blogs.Extensions; +using Shouldly; + +namespace Volo.CmsKit.Blogs +{ + public class UrlSlugExtensions_Tests + { + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly() + { + // Arrange + var name = "My awesome name"; + var expected = "my-awesome-name"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithDot() + { + // Arrange + var name = "My Perfect Title v.2"; + var expected = "my-perfect-title-v2"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithQuestionMark() + { + // Arrange + var name = "Are you gonna die?"; + var expected = "are-you-gonna-die"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithAmpersand() + { + // Arrange + var name = "We & Machines Challenge"; + var expected = "we-machines-challenge"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithDoubleDash() + { + // Arrange + var name = "Go and Code --part 2"; + var expected = "go-and-code-part-2"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithCyrillicChars() + { + // Arrange + var name = "Мое классное название"; + var expected = "moe-klassnoe-nazvanie"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithTurkishChars() + { + // Arrange + var name = "Özel Türkçe karakterler: ğüşiöç"; + var expected = "ozel-turkce-karakterler-gusioc"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithChineseChars() + { + // Arrange + var name = "我的挑战"; + var expected = "o-e-iao-han"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public void NormalizeAsUrlSlug_ShouldWorkProperly_WithEmoji() + { + // Arrange + var name = "Let's Rock 👊"; + var expected = "lets-rock"; + + // Act + var actual = name.NormalizeAsUrlSlug(); + + // Assert + actual.ShouldBe(expected); + } + } +} diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs index aaed5e3d16..9b1f820c23 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitDataSeedContributor.cs @@ -308,7 +308,7 @@ namespace Volo.CmsKit private async Task SeedBlogsAsync() { - var blog = await _blogRepository.InsertAsync(new Blog(_cmsKitTestData.Blog_Id, _cmsKitTestData.BlogName)); + var blog = await _blogRepository.InsertAsync(new Blog(_cmsKitTestData.Blog_Id, _cmsKitTestData.BlogName, _cmsKitTestData.BlogUrlSlug)); await _blogPostRepository.InsertAsync(new BlogPost(_cmsKitTestData.BlogPost_1_Id, blog.Id, _cmsKitTestData.BlogPost_1_Title, _cmsKitTestData.BlogPost_1_UrlSlug, "Short desc 1")); diff --git a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs index 2a0cc0e8bf..99682fbbfb 100644 --- a/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs +++ b/modules/cms-kit/test/Volo.CmsKit.TestBase/CmsKitTestData.cs @@ -77,6 +77,8 @@ namespace Volo.CmsKit public string BlogName => "Cms Blog"; + public string BlogUrlSlug => "cms-blog"; + public Guid BlogPost_1_Id { get; } = Guid.NewGuid(); public string BlogPost_1_Title => "How to install CmsKit?";