diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs index 1a4a657680..4f3ad07f7b 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/Blogs/BlogPostConsts.cs @@ -5,5 +5,9 @@ public static int MaxTitleLength { get; set; } = 256; public static int MaxCoverImageUrlLength { get; set; } = 2048; + + public static int MaxUrlSlugLength { get; set; } = 256; + + public static int MinUrlSlugLength { get; set; } = 2; } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs index 67042b5cc6..4edf42dcf8 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain.Shared/Volo/CmsKit/CmsKitErrorCodes.cs @@ -10,5 +10,10 @@ { public const string UrlAlreadyExist = "CmsKit:Page:0001"; } + + public static class Blogs + { + public const string UrlSlugAlreadyExist = "CmsKit:BlogPost:0001"; + } } } 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 f15dd87f75..ac7c1221c0 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 @@ -1,13 +1,61 @@ -using System; +using JetBrains.Annotations; +using System; +using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; +using Volo.CmsKit.Blogs; namespace Volo.CmsKit.Domain.Volo.CmsKit.Blogs { public class BlogPost : FullAuditedEntity { - public Guid BlogId { get; set; } - public string Title { get; set; } + public Guid BlogId { get; protected set; } + + public string Title { get; protected set; } + + public string UrlSlug { get; protected set; } + public string CoverImageUrl { get; set; } - public bool IsPublished { get; set; } + + public bool IsPublished { get; protected set; } + + public DateTime? PublishDate { get; protected set; } + + protected BlogPost() + { + } + + public BlogPost( + Guid blogId, + [NotNull] string title, + [NotNull] string urlSlug, + [CanBeNull] string coverImageUrl = null, + bool isPublished = true) + { + BlogId = blogId; + SetTitle(title); + SetUrlSlug(urlSlug); + CoverImageUrl = coverImageUrl; + IsPublished = isPublished; + } + + public void SetTitle(string title) + { + Title = Check.NotNullOrWhiteSpace(title, nameof(title), BlogPostConsts.MaxTitleLength); + } + + public void SetUrlSlug(string urlSlug) + { + UrlSlug = Check.NotNullOrWhiteSpace(urlSlug, nameof(urlSlug), BlogPostConsts.MaxUrlSlugLength, BlogPostConsts.MinUrlSlugLength); + } + + public void SetIsPublished(bool isPublished) + { + if (!IsPublished && isPublished) + { + this.PublishDate = DateTime.UtcNow; + } + + IsPublished = IsPublished; + } } } diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs new file mode 100644 index 0000000000..f3a268e956 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostManager.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Domain.Volo.CmsKit.Blogs +{ + public class BlogPostManager : IBlogPostManager + { + protected readonly IBlogPostRepository _blogPostRepository; + + public BlogPostManager(IBlogPostRepository blogPostRepository) + { + _blogPostRepository = blogPostRepository; + } + + public async Task CreateAsync(BlogPost blogPost) + { + await CheckUrlSlugExistenceAsync(blogPost.UrlSlug); + + return await _blogPostRepository.InsertAsync(blogPost); + } + + public async Task UpdateAsync(BlogPost blogPost) + { + await CheckUrlSlugExistenceAsync(blogPost.UrlSlug); + + await _blogPostRepository.UpdateAsync(blogPost); + } + + private async Task CheckUrlSlugExistenceAsync(string urlSlug) + { + if (await _blogPostRepository.SlugExistsAsync(urlSlug)) + { + throw new BlogPostUrlSlugAlreadyExistException(urlSlug); + } + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostUrlSlugAlreadyExistException.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostUrlSlugAlreadyExistException.cs new file mode 100644 index 0000000000..bccffef418 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogPostUrlSlugAlreadyExistException.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp; + +namespace Volo.CmsKit.Domain.Volo.CmsKit.Blogs +{ + public class BlogPostUrlSlugAlreadyExistException : BusinessException + { + internal BlogPostUrlSlugAlreadyExistException(string code = null, string message = null, string details = null, Exception innerException = null, Microsoft.Extensions.Logging.LogLevel logLevel = Microsoft.Extensions.Logging.LogLevel.Warning) : base(code, message, details, innerException, logLevel) + { + } + + internal BlogPostUrlSlugAlreadyExistException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext context) : base(serializationInfo, context) + { + } + + public BlogPostUrlSlugAlreadyExistException(string urlSlug) + { + Code = CmsKitErrorCodes.Blogs.UrlSlugAlreadyExist; + + WithData(nameof(BlogPost.UrlSlug), urlSlug); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs new file mode 100644 index 0000000000..516e8f7835 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostManager.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.CmsKit.Domain.Volo.CmsKit.Blogs +{ + public interface IBlogPostManager + { + Task CreateAsync(BlogPost blogPost); + + Task UpdateAsync(BlogPost blogPost); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs new file mode 100644 index 0000000000..dfd0ec7977 --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/IBlogPostRepository.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; + +namespace Volo.CmsKit.Domain.Volo.CmsKit.Blogs +{ + public interface IBlogPostRepository : IBasicRepository + { + Task SlugExistsAsync(string slug); + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/BlogPostEfCoreRepository.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/BlogPostEfCoreRepository.cs new file mode 100644 index 0000000000..79a2bbe50f --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/Blogs/BlogPostEfCoreRepository.cs @@ -0,0 +1,24 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.EntityFrameworkCore; +using Volo.Abp.EntityFrameworkCore; +using Volo.CmsKit.Domain.Volo.CmsKit.Blogs; +using Volo.CmsKit.EntityFrameworkCore; + +namespace Volo.CmsKit.Blogs +{ + public class BlogPostEfCoreRepository : EfCoreRepository, IBlogPostRepository + { + public BlogPostEfCoreRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task SlugExistsAsync(string slug) + { + var dbSet = await GetDbSetAsync(); + + return await dbSet.AnyAsync(x => x.UrlSlug.ToLower() == slug); + } + } +} diff --git a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs index c7180fb098..79a245e693 100644 --- a/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs +++ b/modules/cms-kit/src/Volo.CmsKit.EntityFrameworkCore/Volo/CmsKit/EntityFrameworkCore/CmsKitDbContextModelCreatingExtensions.cs @@ -15,6 +15,7 @@ using Volo.CmsKit.Ratings; using Volo.CmsKit.Tags; using Volo.CmsKit.Domain.Volo.CmsKit.Blogs; using Volo.CmsKit.Blogs; +using System.Security.Cryptography.X509Certificates; namespace Volo.CmsKit.EntityFrameworkCore { @@ -228,7 +229,11 @@ namespace Volo.CmsKit.EntityFrameworkCore b.Property(p => p.Title).IsRequired().HasMaxLength(BlogPostConsts.MaxTitleLength); + b.Property(p => p.UrlSlug).IsRequired().HasMaxLength(BlogPostConsts.MaxUrlSlugLength); + b.Property(p => p.CoverImageUrl).HasMaxLength(BlogPostConsts.MaxCoverImageUrlLength); + + b.HasIndex(x => x.UrlSlug); }); } } diff --git a/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/BlogPostMongoRepository.cs b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/BlogPostMongoRepository.cs new file mode 100644 index 0000000000..55eb84e1bb --- /dev/null +++ b/modules/cms-kit/src/Volo.CmsKit.MongoDB/Volo/CmsKit/MongoDB/Blogs/BlogPostMongoRepository.cs @@ -0,0 +1,22 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; +using Volo.CmsKit.Domain.Volo.CmsKit.Blogs; + +namespace Volo.CmsKit.MongoDB.Blogs +{ + public class BlogPostMongoRepository : MongoDbRepository, IBlogPostRepository + { + public BlogPostMongoRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + { + } + + public async Task SlugExistsAsync(string slug) + { + var queryable = await GetQueryableAsync(); + + return await AsyncExecuter.AnyAsync(queryable, x => x.UrlSlug.ToLower() == slug); + } + } +}