diff --git a/modules/blogging/app/Volo.BloggingTestApp/Pages/_ViewImports.cshtml b/modules/blogging/app/Volo.BloggingTestApp/Pages/_ViewImports.cshtml new file mode 100644 index 0000000000..caae8c9ac5 --- /dev/null +++ b/modules/blogging/app/Volo.BloggingTestApp/Pages/_ViewImports.cshtml @@ -0,0 +1,5 @@ +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap +@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling +@addTagHelper *, Volo.Blogging.Web \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Members/IMemberAppService.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Members/IMemberAppService.cs new file mode 100644 index 0000000000..5e06d20956 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Members/IMemberAppService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Services; +using Volo.Blogging.Posts; + +namespace Volo.Blogging.Members; + +public interface IMemberAppService : IApplicationService +{ + Task FindAsync(string username); +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs index 8f1f2c251d..e4af45a345 100644 --- a/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs +++ b/modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; @@ -20,5 +21,7 @@ namespace Volo.Blogging.Posts Task CreateAsync(CreatePostDto input); Task UpdateAsync(Guid id, UpdatePostDto input); + + Task> GetListByUserIdAsync(Guid userId); } } diff --git a/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Members/MemberAppService.cs b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Members/MemberAppService.cs new file mode 100644 index 0000000000..e27c0baefa --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Members/MemberAppService.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Repositories; +using Volo.Blogging.Posts; +using Volo.Blogging.Users; + +namespace Volo.Blogging.Members; + +public class MemberAppService : BloggingAppServiceBase, IMemberAppService +{ + private readonly IRepository _userRepository; + + public MemberAppService(IRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task FindAsync(string username) + { + var user = await _userRepository.FindAsync(x => x.UserName == username); + + if (user == null) + { + return null; + } + + return ObjectMapper.Map(user); + } +} \ No newline at end of file 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 15506ecf70..0158dc0e5a 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 @@ -188,6 +188,13 @@ namespace Volo.Blogging.Posts return ObjectMapper.Map(post); } + public async Task> GetListByUserIdAsync(Guid userId) + { + var posts = await PostRepository.GetListByUserIdAsync(userId); + + return ObjectMapper.Map, List>(posts); + } + [Authorize(BloggingPermissions.Posts.Create)] public async Task CreateAsync(CreatePostDto input) { 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 b9f4e44bde..d4bfede795 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 @@ -59,6 +59,8 @@ "ClearCacheConfirmationMessage": "Are you sure you want to clear the cache?", "MarkdownSupported": "Markdown is supported", "FileUploadInfo": "Drag, drop, or paste a copied image.", - "PostDescriptionHint": "* Will be rendered in the article link preview, supports HTML" + "PostDescriptionHint": "* Will be rendered in the article link preview, supports HTML", + "ReadMore": "Continue Reading", + "MemberNotPublishedPostYet": "No posts yet!" } -} \ No newline at end of file +} diff --git a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs index b5446515bf..33a6377264 100644 --- a/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.Domain/Volo/Blogging/Posts/IPostRepository.cs @@ -15,5 +15,7 @@ namespace Volo.Blogging.Posts Task GetPostByUrl(Guid blogId, string url, CancellationToken cancellationToken = default); Task> GetOrderedList(Guid blogId,bool descending = false, CancellationToken cancellationToken = default); + + Task> GetListByUserIdAsync(Guid userId, CancellationToken cancellationToken = default); } } diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs index a676816dee..48dfabb77e 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/Posts/EfCorePostRepository.cs @@ -8,6 +8,7 @@ using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using Volo.Blogging.EntityFrameworkCore; +using Volo.Blogging.Users; namespace Volo.Blogging.Posts { @@ -61,6 +62,14 @@ namespace Volo.Blogging.Posts } + public async Task> GetListByUserIdAsync(Guid userId, CancellationToken cancellationToken = default) + { + var query = (await GetDbSetAsync()).Where(p => p.CreatorId == userId) + .OrderByDescending(p => p.CreationTime); + + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } + public override async Task> WithDetailsAsync() { return (await GetQueryableAsync()).IncludeDetails(); diff --git a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.Generated.cs b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.Generated.cs new file mode 100644 index 0000000000..dfabaed346 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.Generated.cs @@ -0,0 +1,28 @@ +// This file is automatically generated by ABP framework to use MVC Controllers from CSharp +using System; +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Http.Client; +using Volo.Abp.Http.Modeling; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Http.Client.ClientProxying; +using Volo.Blogging.Blogs; +using Volo.Blogging.Blogs.Dtos; +using Volo.Blogging.Members; +using Volo.Blogging.Posts; + +// ReSharper disable once CheckNamespace +namespace Volo.Blogging.ClientProxies; + +[Dependency(ReplaceServices = true)] +[ExposeServices(typeof(IMemberAppService), typeof(MembersClientProxy))] +public partial class MembersClientProxy : ClientProxyBase, IMemberAppService +{ + public Task FindAsync(string username) + { + return RequestAsync(nameof(FindAsync), new ClientProxyRequestTypeValue + { + { typeof(string), username } + }); + } +} diff --git a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.cs b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.cs new file mode 100644 index 0000000000..61e4f3df1e --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/MembersClientProxy.cs @@ -0,0 +1,7 @@ +// This file is part of MembersClientProxy, you can customize it here +// ReSharper disable once CheckNamespace +namespace Volo.Blogging.ClientProxies; + +public partial class MembersClientProxy +{ +} diff --git a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/PostsClientProxy.Generated.cs b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/PostsClientProxy.Generated.cs index 17ee93818a..697606f05c 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/PostsClientProxy.Generated.cs +++ b/modules/blogging/src/Volo.Blogging.HttpApi.Client/ClientProxies/PostsClientProxy.Generated.cs @@ -1,5 +1,6 @@ // This file is automatically generated by ABP framework to use MVC Controllers from CSharp using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Http.Client; @@ -65,6 +66,14 @@ public partial class PostsClientProxy : ClientProxyBase, IPostA }); } + public virtual async Task> GetListByUserIdAsync(Guid userId) + { + return await RequestAsync>(nameof(GetListByUserIdAsync), new ClientProxyRequestTypeValue + { + { typeof(Guid), userId } + }); + } + public virtual async Task DeleteAsync(Guid id) { await RequestAsync(nameof(DeleteAsync), new ClientProxyRequestTypeValue diff --git a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs index 03cd460ed2..f2f31261a6 100644 --- a/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs +++ b/modules/blogging/src/Volo.Blogging.HttpApi/Volo/Blogging/PostsController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Volo.Abp; @@ -60,6 +61,13 @@ namespace Volo.Blogging { return _postAppService.UpdateAsync(id, input); } + + [HttpGet] + [Route("user/{userId}")] + public Task> GetListByUserIdAsync(Guid userId) + { + return _postAppService.GetListByUserIdAsync(userId); + } [HttpDelete] [Route("{id}")] diff --git a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs index a2d79c9466..be6942eac2 100644 --- a/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs +++ b/modules/blogging/src/Volo.Blogging.MongoDB/Volo/Blogging/Posts/MongoPostRepository.cs @@ -58,5 +58,13 @@ namespace Volo.Blogging.Posts return await query.OrderByDescending(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken)); } + + public async Task> GetListByUserIdAsync(Guid userId, CancellationToken cancellationToken = default) + { + var query = (await GetMongoQueryableAsync(cancellationToken)).Where(x => x.CreatorId == userId) + .OrderByDescending(x => x.CreationTime); + + return await query.ToListAsync(GetCancellationToken(cancellationToken)); + } } } diff --git a/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs b/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs index 1391a5de19..dd8d65ea6b 100644 --- a/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs +++ b/modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs @@ -95,6 +95,7 @@ namespace Volo.Blogging options.Conventions.AddPageRoute("/Blogs/Posts/Detail", routePrefix + "{blogShortName:blogNameConstraint}/{postUrl}"); options.Conventions.AddPageRoute("/Blogs/Posts/Edit", routePrefix + "{blogShortName}/posts/{postId}/edit"); options.Conventions.AddPageRoute("/Blogs/Posts/New", routePrefix + "{blogShortName}/posts/new"); + options.Conventions.AddPageRoute("/Members/Index", routePrefix + "members/{userName}"); }); Configure(options => diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml index 5912e4e6c9..f893907e66 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Detail.cshtml @@ -65,14 +65,19 @@
@if (Model.Post.Writer != null) { - + + + }
@if (Model.Post.Writer != null) { -
@(Model.Post.Writer.UserName) @BloggingPageHelper.ConvertDatetimeToTimeAgo(Model.Post.CreationTime)
- + +
+ @(Model.Post.Writer.UserName) @BloggingPageHelper.ConvertDatetimeToTimeAgo(Model.Post.CreationTime) +
+
} @L["WiewsWithCount", @Model.Post.ReadCount] diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml index 03514aaf3a..78c466e8c9 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Blogs/Posts/Index.cshtml @@ -1,5 +1,6 @@ @page @using Microsoft.AspNetCore.Authorization +@using Volo.Blogging.Areas.Blog.Helpers.TagHelpers @using Volo.Abp.AspNetCore.Mvc.UI.Packages.OwlCarousel @using Volo.Blogging @inject IAuthorizationService Authorization @@ -59,11 +60,17 @@
- + + +
-
@post.Writer.UserName @BloggingPageHelper.ConvertDatetimeToTimeAgo(post.CreationTime)
- @L["WiewsWithCount", post.ReadCount] + +
+ @post.Writer.UserName @BloggingPageHelper.ConvertDatetimeToTimeAgo(post.CreationTime) +
+
+ @L["WiewsWithCount", post.ReadCount] @*| @L["CommentWithCount", post.CommentCount]*@
diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml new file mode 100644 index 0000000000..62841ae158 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml @@ -0,0 +1,96 @@ +@page +@using Microsoft.Extensions.Localization +@using Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers +@using Volo.Abp.Users +@using Volo.Blogging.Localization +@model Volo.Blogging.Pages.Members.IndexModel +@inject IStringLocalizer L +@inject ICurrentUser CurrentUser +@{ + ViewBag.Title = @Model.User.UserName.ToUpper() + " - " + L["Blogs"].Value; +} + +@section styles { + +} + +
+
+
+
+
+
+
+ +
+ @if (Model.User.UserName != null) + { +

@Model.User.UserName

+ } + @L["UserName"].Value.ToUpper() +
@Model.User.UserName
+
+
+
+ @if (Model.Posts is not null && Model.Posts.Any()) + { +
+ + +
+ @foreach (var post in Model.Posts) + { +
+
+ + + + + + + @L["Blog"].Value.ToUpper() + +
+
+ +

+ + @post.Title + +

+

+ + @post.Description.TruncateWithPostfix(150) + + @L["ReadMore"] +

+
+
+
+ +
+
+
+ } +
+
+
+
+ } + else + { +
+
+

@L["MemberNotPublishedPostYet"]

+
+
+ } +
+
+
\ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml.cs b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml.cs new file mode 100644 index 0000000000..8018ea23e8 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.cshtml.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Blogging.Blogs; +using Volo.Blogging.Members; +using Volo.Blogging.Posts; + +namespace Volo.Blogging.Pages.Members; + +public class IndexModel : AbpPageModel +{ + private readonly IPostAppService _postAppService; + + private readonly IMemberAppService _memberAppService; + + private readonly IBlogAppService _blogAppService; + + public BlogUserDto User { get; set; } + public List Posts { get; set; } + + public Dictionary BlogShortNameMap { get; set; } + + public IndexModel(IPostAppService postAppService, IMemberAppService memberAppService, IBlogAppService blogAppService) + { + _postAppService = postAppService; + _memberAppService = memberAppService; + _blogAppService = blogAppService; + } + + public async Task OnGetAsync(string userName) + { + User = await _memberAppService.FindAsync(userName); + + if (User is null) + { + return Redirect("/"); + } + + Posts = await _postAppService.GetListByUserIdAsync(User.Id); + + var blogIds = Posts.Select(x => x.BlogId).Distinct(); + BlogShortNameMap = new Dictionary(); + + foreach (var blogId in blogIds) + { + BlogShortNameMap[blogId] = (await _blogAppService.GetAsync(blogId)).ShortName; + } + + return Page(); + } + public string GetBlogPostUrl(PostWithDetailsDto post) + { + var blogShortName = BlogShortNameMap[post.BlogId]; + + return "/" + blogShortName + "/" + post.Url; + } + + public string GetMemberProfileUrl(BlogUserDto user) + { + return "/members/" + user.UserName; + } + +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.css b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.css new file mode 100644 index 0000000000..4c5167a694 --- /dev/null +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Members/Index.css @@ -0,0 +1,3 @@ +.post-desc { + overflow-wrap: break-word; +} \ No newline at end of file diff --git a/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js b/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js index c2872c74bf..f3358106d3 100644 --- a/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js +++ b/modules/blogging/src/Volo.Blogging.Web/wwwroot/client-proxies/blogging-proxy.js @@ -160,6 +160,12 @@ }, ajaxParams)); }; + volo.blogging.posts.getListByUserId = function (userId, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/blogging/posts/user/' + userId + '', + type: 'GET' + }, ajaxParams)); + }; })(); // controller volo.blogging.tags @@ -176,7 +182,20 @@ }; })(); - + + // controller volo.blogging.members + + (function() { + abp.utils.createNamespace(window, 'volo.blogging.members'); + + volo.blogging.members.get = function (username, ajaxParams) { + return abp.ajax($.extend(true, { + url: abp.appPath + 'api/blogging/members/' + username + '', + type: 'GET' + }, ajaxParams)); + }; + })(); + })();