diff --git a/modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/Blogs/IBlogManagementAppService.cs b/modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/Blogs/IBlogManagementAppService.cs index 35d6d1c97e..1e363c8578 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/Blogs/IBlogManagementAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Application.Contracts/Volo/Blogging/Admin/Blogs/IBlogManagementAppService.cs @@ -18,5 +18,7 @@ namespace Volo.Blogging.Admin.Blogs Task UpdateAsync(Guid id, UpdateBlogDto input); Task DeleteAsync(Guid id); + + Task ClearCacheAsync(Guid id); } } diff --git a/modules/blogging/src/Volo.Blogging.Admin.Application/Volo/Blogging/Admin/Blogs/BlogManagementAppService.cs b/modules/blogging/src/Volo.Blogging.Admin.Application/Volo/Blogging/Admin/Blogs/BlogManagementAppService.cs index b11bd481fe..1c5f282d7c 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Application/Volo/Blogging/Admin/Blogs/BlogManagementAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.Application/Volo/Blogging/Admin/Blogs/BlogManagementAppService.cs @@ -3,18 +3,22 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; +using Volo.Abp.Caching; using Volo.Blogging.Blogs; using Volo.Blogging.Blogs.Dtos; +using Volo.Blogging.Posts; namespace Volo.Blogging.Admin.Blogs { public class BlogManagementAppService : BloggingAdminAppServiceBase, IBlogManagementAppService { private readonly IBlogRepository _blogRepository; - - public BlogManagementAppService(IBlogRepository blogRepository) + private readonly IDistributedCache> _postsCache; + + public BlogManagementAppService(IBlogRepository blogRepository, IDistributedCache> postsCache) { _blogRepository = blogRepository; + _postsCache = postsCache; } public async Task> GetListAsync() @@ -63,5 +67,11 @@ namespace Volo.Blogging.Admin.Blogs { await _blogRepository.DeleteAsync(id); } + + [Authorize(BloggingPermissions.Blogs.ClearCache)] + public async Task ClearCacheAsync(Guid id) + { + await _postsCache.RemoveAsync(id.ToString()); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs b/modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs index 815604e871..ce6457dcc7 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs +++ b/modules/blogging/src/Volo.Blogging.Admin.HttpApi/Volo/Blogging/Admin/BlogManagementController.cs @@ -54,5 +54,12 @@ namespace Volo.Blogging.Admin { await _blogManagementAppService.DeleteAsync(id); } + + [HttpGet] + [Route("clear-cache/{id}")] + public async Task ClearCacheAsync(Guid id) + { + await _blogManagementAppService.ClearCacheAsync(id); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/index.js b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/index.js index a2b618b89f..aaa378cabd 100644 --- a/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/index.js +++ b/modules/blogging/src/Volo.Blogging.Admin.Web/Pages/Blogging/Admin/Blogs/index.js @@ -52,6 +52,22 @@ }); }, }, + { + text: l("ClearCache"), + visible: abp.auth.isGranted( + 'Blogging.Blog.ClearCache' + ), + confirmMessage: function (data) { + return l("ClearCacheConfirmationMessage"); + }, + action: function (data) { + volo.blogging.admin.blogManagement + .clearCache(data.record.id) + .then(function () { + _dataTable.ajax.reload(); + }) + } + } ], }, }, diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissionDefinitionProvider.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissionDefinitionProvider.cs index 65a6ab1ab7..595e32199c 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissionDefinitionProvider.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissionDefinitionProvider.cs @@ -15,6 +15,7 @@ namespace Volo.Blogging blogs.AddChild(BloggingPermissions.Blogs.Update, L("Permission:Edit")); blogs.AddChild(BloggingPermissions.Blogs.Delete, L("Permission:Delete")); blogs.AddChild(BloggingPermissions.Blogs.Create, L("Permission:Create")); + blogs.AddChild(BloggingPermissions.Blogs.ClearCache, L("Permission:ClearCache")); var posts = bloggingGroup.AddPermission(BloggingPermissions.Posts.Default, L("Permission:Posts")); posts.AddChild(BloggingPermissions.Posts.Update, L("Permission:Edit")); diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissions.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissions.cs index fae85acf88..617b4d06f1 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissions.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts.Shared/Volo/Blogging/BloggingPermissions.cs @@ -13,6 +13,7 @@ namespace Volo.Blogging public const string Delete = Default + ".Delete"; public const string Update = Default + ".Update"; public const string Create = Default + ".Create"; + public const string ClearCache = Default + ".ClearCache"; } public static class Posts diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs index b9e3cf0dea..2d40c7fad9 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs @@ -20,6 +20,16 @@ namespace Volo.Blogging CreateMap().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount).Ignore(x=>x.Tags); CreateMap().Ignore(x => x.Writer); CreateMap(); + CreateMap().Ignore(x=>x.Writer).Ignore(x=>x.CommentCount).Ignore(x=>x.Tags); + CreateMap() + .Ignore(x=>x.LastModificationTime) + .Ignore(x=>x.LastModifierId) + .Ignore(x=>x.DeleterId) + .Ignore(x=>x.IsDeleted) + .Ignore(x=>x.DeletionTime) + .Ignore(x=>x.CommentCount) + .Ignore(x=>x.Tags); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs index 07d22d3385..9e8acba57d 100644 --- a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs @@ -20,9 +20,15 @@ namespace Volo.Blogging.Posts private readonly IPostRepository _postRepository; private readonly ITagRepository _tagRepository; private readonly ICommentRepository _commentRepository; - private readonly IDistributedCache> _postsCache; - - public PostAppService(IPostRepository postRepository, ITagRepository tagRepository, ICommentRepository commentRepository, IBlogUserLookupService userLookupService, IDistributedCache> postsCache) + private readonly IDistributedCache> _postsCache; + + public PostAppService( + IPostRepository postRepository, + ITagRepository tagRepository, + ICommentRepository commentRepository, + IBlogUserLookupService userLookupService, + IDistributedCache> postsCache + ) { UserLookupService = userLookupService; _postRepository = postRepository; @@ -33,21 +39,47 @@ namespace Volo.Blogging.Posts public async Task> GetListByBlogIdAndTagName(Guid id, string tagName) { - var cacheKey = id.ToString() + "-" + tagName; + var posts = await _postRepository.GetPostsByBlogId(id); + var tag = tagName.IsNullOrWhiteSpace() ? null : await _tagRepository.FindByNameAsync(id, tagName); + var userDictionary = new Dictionary(); + var postDtos = new List(ObjectMapper.Map, List>(posts)); - return await _postsCache.GetOrAddAsync( - cacheKey, - async () => await GetPostsByBlogIdAndTagName(id, tagName), - () => new DistributedCacheEntryOptions + foreach (var postDto in postDtos) + { + postDto.Tags = await GetTagsOfPost(postDto.Id); + } + + if (tag != null) + { + postDtos = await FilterPostsByTag(postDtos, tag); + } + + foreach (var postDto in postDtos) + { + if (postDto.CreatorId.HasValue) { - AbsoluteExpiration = DateTimeOffset.Now.AddHours(6) + if (!userDictionary.ContainsKey(postDto.CreatorId.Value)) + { + var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value); + if (creatorUser != null) + { + userDictionary[creatorUser.Id] = ObjectMapper.Map(creatorUser); + } + } + + if (userDictionary.ContainsKey(postDto.CreatorId.Value)) + { + postDto.Writer = userDictionary[(Guid)postDto.CreatorId]; + } } - ); + } + + return new ListResultDto(postDtos); } public async Task> GetTimeOrderedListAsync(Guid blogId) { - return await _postsCache.GetOrAddAsync( + var postCacheItems = await _postsCache.GetOrAddAsync( blogId.ToString(), async () => await GetTimeOrderedPostsAsync(blogId), () => new DistributedCacheEntryOptions @@ -55,6 +87,22 @@ namespace Volo.Blogging.Posts AbsoluteExpiration = DateTimeOffset.Now.AddHours(6) } ); + + var postsWithDetails = ObjectMapper.Map, List>(postCacheItems); + + foreach (var post in postsWithDetails) + { + if (post.CreatorId.HasValue) + { + var creatorUser = await UserLookupService.FindByIdAsync(post.CreatorId.Value); + if (creatorUser != null) + { + post.Writer = ObjectMapper.Map(creatorUser); + } + } + } + + return new ListResultDto(postsWithDetails); } public async Task GetForReadingAsync(GetPostInput input) @@ -157,62 +205,13 @@ namespace Volo.Blogging.Posts return ObjectMapper.Map(post); } - private async Task> GetPostsByBlogIdAndTagName(Guid id, string tagName) - { - var posts = await _postRepository.GetPostsByBlogId(id); - var tag = tagName.IsNullOrWhiteSpace() ? null : await _tagRepository.FindByNameAsync(id, tagName); - var userDictionary = new Dictionary(); - var postDtos = new List(ObjectMapper.Map, List>(posts)); - - foreach (var postDto in postDtos) - { - postDto.Tags = await GetTagsOfPost(postDto.Id); - } - - if (tag != null) - { - postDtos = await FilterPostsByTag(postDtos, tag); - } - - foreach (var postDto in postDtos) - { - if (postDto.CreatorId.HasValue) - { - if (!userDictionary.ContainsKey(postDto.CreatorId.Value)) - { - var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value); - if (creatorUser != null) - { - userDictionary[creatorUser.Id] = ObjectMapper.Map(creatorUser); - } - } - - if (userDictionary.ContainsKey(postDto.CreatorId.Value)) - { - postDto.Writer = userDictionary[(Guid)postDto.CreatorId]; - } - } - } - - return new ListResultDto(postDtos); - } - - private async Task> GetTimeOrderedPostsAsync(Guid blogId) + private async Task> GetTimeOrderedPostsAsync(Guid blogId) { var posts = await _postRepository.GetOrderedList(blogId); - var postDtos = new List(ObjectMapper.Map, List>(posts)); + var postCacheItems = ObjectMapper.Map, List>(posts); - foreach (var postDto in postDtos) - { - var creatorUser = await UserLookupService.FindByIdAsync(postDto.CreatorId.Value); - if (creatorUser != null) - { - postDto.Writer = ObjectMapper.Map(creatorUser); - } - } - - return new ListResultDto(postDtos); + return postCacheItems; } private async Task RenameUrlIfItAlreadyExistAsync(Guid blogId, string url, Post existingPost = null) diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json index 5ebdf25d9f..2219aa9fcd 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/en.json @@ -12,6 +12,7 @@ "Permission:Posts": "Posts", "Permission:Tags": "Tags", "Permission:Comments": "Comments", + "Permission:ClearCache": "Clear cache", "Title": "Title", "Delete": "Delete", "Reply": "Reply", @@ -53,6 +54,8 @@ "Blogs": "Blogs", "Tags": "Tags", "ShareOn": "Share on", - "TitleLengthWarning": "Keep your title size under 60 characters to be SEO friendly!" + "TitleLengthWarning": "Keep your title size under 60 characters to be SEO friendly!", + "ClearCache": "Clear cache", + "ClearCacheConfirmationMessage": "Are you sure you want to clear the cache?" } } diff --git a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json index 4a9a38b040..915e8b233e 100644 --- a/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json +++ b/modules/blogging/src/Volo.Blogging.Domain.Shared/Volo/Blogging/Localization/Resources/tr.json @@ -12,6 +12,7 @@ "Permission:Posts": "Yazılar", "Permission:Tags": "Etiketler", "Permission:Comments": "Yorumlar", + "Permission:ClearCache": "Önbelleği temizle", "Title": "Başlık", "Delete": "Sil", "Reply": "Yanıtla", @@ -53,6 +54,8 @@ "Blogs": "Bloglar", "Tags": "Etiketler", "ShareOn": "Paylaş", - "TitleLengthWarning": "Başlığınınz SEO dostu olabilmesi için 60 karakterden az olmasını sağlayın!" + "TitleLengthWarning": "Başlığınınz SEO dostu olabilmesi için 60 karakterden az olmasını sağlayın!", + "ClearCache": "Önbelleği temizle", + "ClearCacheConfirmationMessage": "Önbelleği temizlemek istediğinizden emin misiniz?" } } diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj b/modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj index 73cf0498c5..c6c096105b 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo.Blogging.Domain.csproj @@ -15,6 +15,7 @@ + diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/BloggingDomainModule.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/BloggingDomainModule.cs index 3179303215..2e5e9a68e0 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/BloggingDomainModule.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/BloggingDomainModule.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AutoMapper; +using Volo.Abp.Caching; using Volo.Abp.Domain; using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Modularity; @@ -13,7 +14,9 @@ namespace Volo.Blogging [DependsOn( typeof(BloggingDomainSharedModule), typeof(AbpDddDomainModule), - typeof(AbpAutoMapperModule))] + typeof(AbpAutoMapperModule), + typeof(AbpCachingModule) + )] public class BloggingDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheInvalidator.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheInvalidator.cs new file mode 100644 index 0000000000..abbfcd8d88 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheInvalidator.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; + +namespace Volo.Blogging.Posts +{ + public class PostCacheInvalidator : ILocalEventHandler>, ITransientDependency + { + protected IDistributedCache> Cache { get; } + + public PostCacheInvalidator(IDistributedCache> cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var cacheKey = eventData.Entity.BlogId.ToString(); + await Cache.RemoveAsync(cacheKey); + } + } +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheItem.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheItem.cs new file mode 100644 index 0000000000..f08424aaf1 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/PostCacheItem.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo.Abp.Auditing; +using Volo.Abp.Domain.Entities; +using Volo.Blogging.Tagging; +using Volo.Blogging.Users; + +namespace Volo.Blogging.Posts +{ + [Serializable] + public class PostCacheItem + { + public Guid Id { get; set; } + public Guid BlogId { get; set; } + + public string Title { get; set; } + + public string CoverImage { get; set; } + + public string Url { get; set; } + + public string Content { get; set; } + + public string Description { get; set; } + + public int ReadCount { get; set; } + + public int CommentCount { get; set; } + + [CanBeNull] + public BlogUser Writer { get; set; } + + public List Tags { get; set; } + public Guid? CreatorId { get; set; } + + public DateTime CreationTime { get; set; } + } +} \ No newline at end of file