Merge pull request #16313 from abpframework/Onur/blogging-module-add-profile

Blog Module - Member page created
pull/16378/head
Alper Ebiçoğlu 3 years ago committed by GitHub
commit 664788516e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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

@ -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<BlogUserDto> FindAsync(string username);
}

@ -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<PostWithDetailsDto> CreateAsync(CreatePostDto input);
Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input);
Task<List<PostWithDetailsDto>> GetListByUserIdAsync(Guid userId);
}
}

@ -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<BlogUser, Guid> _userRepository;
public MemberAppService(IRepository<BlogUser, Guid> userRepository)
{
_userRepository = userRepository;
}
public async Task<BlogUserDto> FindAsync(string username)
{
var user = await _userRepository.FindAsync(x => x.UserName == username);
if (user == null)
{
return null;
}
return ObjectMapper.Map<BlogUser, BlogUserDto>(user);
}
}

@ -188,6 +188,13 @@ namespace Volo.Blogging.Posts
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
public async Task<List<PostWithDetailsDto>> GetListByUserIdAsync(Guid userId)
{
var posts = await PostRepository.GetListByUserIdAsync(userId);
return ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts);
}
[Authorize(BloggingPermissions.Posts.Create)]
public async Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{

@ -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!"
}
}
}

@ -15,5 +15,7 @@ namespace Volo.Blogging.Posts
Task<Post> GetPostByUrl(Guid blogId, string url, CancellationToken cancellationToken = default);
Task<List<Post>> GetOrderedList(Guid blogId,bool descending = false, CancellationToken cancellationToken = default);
Task<List<Post>> GetListByUserIdAsync(Guid userId, CancellationToken cancellationToken = default);
}
}

@ -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<List<Post>> 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<IQueryable<Post>> WithDetailsAsync()
{
return (await GetQueryableAsync()).IncludeDetails();

@ -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>, IMemberAppService
{
public Task<BlogUserDto> FindAsync(string username)
{
return RequestAsync<BlogUserDto>(nameof(FindAsync), new ClientProxyRequestTypeValue
{
{ typeof(string), username }
});
}
}

@ -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
{
}

@ -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<IPostAppService>, IPostA
});
}
public virtual async Task<List<PostWithDetailsDto>> GetListByUserIdAsync(Guid userId)
{
return await RequestAsync<List<PostWithDetailsDto>>(nameof(GetListByUserIdAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), userId }
});
}
public virtual async Task DeleteAsync(Guid id)
{
await RequestAsync(nameof(DeleteAsync), new ClientProxyRequestTypeValue

@ -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<List<PostWithDetailsDto>> GetListByUserIdAsync(Guid userId)
{
return _postAppService.GetListByUserIdAsync(userId);
}
[HttpDelete]
[Route("{id}")]

@ -58,5 +58,13 @@ namespace Volo.Blogging.Posts
return await query.OrderByDescending(x => x.CreationTime).ToListAsync(GetCancellationToken(cancellationToken));
}
public async Task<List<Post>> 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));
}
}
}

@ -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<DynamicJavaScriptProxyOptions>(options =>

@ -65,14 +65,19 @@
<div class="col-auto pe-1">
@if (Model.Post.Writer != null)
{
<img gravatar-email="@Model.Post.Writer.Email" default-image="Identicon" class="article-avatar" />
<a href="/Members/@Model.Post.Writer.UserName">
<img gravatar-email="@Model.Post.Writer.Email" default-image="Identicon" class="article-avatar"/>
</a>
}
</div>
<div class="col ps-1">
@if (Model.Post.Writer != null)
{
<h5 class="mt-2 mb-1">@(Model.Post.Writer.UserName) <span>@BloggingPageHelper.ConvertDatetimeToTimeAgo(Model.Post.CreationTime)</span></h5>
<a href="/Members/@Model.Post.Writer.UserName">
<h5 class="mt-2 mb-1">
@(Model.Post.Writer.UserName) <span>@BloggingPageHelper.ConvertDatetimeToTimeAgo(Model.Post.CreationTime)</span>
</h5>
</a>
}
<i class="fa fa-eye"></i> @L["WiewsWithCount", @Model.Post.ReadCount]

@ -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 @@
<div class="user-card">
<div class="row">
<div class="col-auto pe-1">
<img gravatar-email="@post.Writer.Email" default-image="Identicon" class="article-avatar" />
<a href="/Members/@post.Writer.UserName">
<img gravatar-email="@post.Writer.Email" default-image="Identicon" class="article-avatar"/>
</a>
</div>
<div class="col ps-1">
<h5 class="mt-2 mb-1">@post.Writer.UserName <span>@BloggingPageHelper.ConvertDatetimeToTimeAgo(post.CreationTime)</span></h5>
<i class="fa fa-eye"></i> @L["WiewsWithCount", post.ReadCount]
<a href="/Members/@post.Writer.UserName">
<h5 class="mt-2 mb-1">
@post.Writer.UserName <span>@BloggingPageHelper.ConvertDatetimeToTimeAgo(post.CreationTime)</span>
</h5>
</a>
<i class="fa fa-eye"></i> @L["WiewsWithCount", post.ReadCount]
@*<span class="vs-seperator">|</span>
<i class="fa fa-comment"></i> @L["CommentWithCount", post.CommentCount]*@
</div>

@ -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<BloggingResource> L
@inject ICurrentUser CurrentUser
@{
ViewBag.Title = @Model.User.UserName.ToUpper() + " - " + L["Blogs"].Value;
}
@section styles {
<abp-style src="/Pages/Members/Index.css"/>
}
<main>
<div class="container">
<div class="row gx-lg-5">
<div class="col-md-4 mb-5 mb-md-0">
<div class="card h-auto member-profile-info">
<div class="card-body">
<div class="d-inline-block position-relative">
<img gravatar-email="@Model.User.Email" default-image="Identicon" class="post-member-img rounded-circle d-block"/>
</div>
@if (Model.User.UserName != null)
{
<h2 class="m-0">@Model.User.UserName</h2>
}
<small class="d-block mt-4">@L["UserName"].Value.ToUpper()</small>
<h5>@Model.User.UserName</h5>
</div>
</div>
</div>
@if (Model.Posts is not null && Model.Posts.Any())
{
<div class="col-md-8">
<abp-tabs>
<abp-tab name="all-posts" title="All Blog Posts">
<div class="mt-4 pt-3">
@foreach (var post in Model.Posts)
{
<div class="post-item">
<div class="post-type-cont">
<a href="@Model.GetMemberProfileUrl(Model.User)" class="text-decoration-none">
<img gravatar-email="@Model.User.Email" default-image="Identicon" class="post-member-img rounded-circle d-block"/>
</a>
<span class="post-type">
<i class="fas fa-pen-nib"></i>
@L["Blog"].Value.ToUpper()
</span>
</div>
<div class="post-detail-cont">
<div class="post-info fs-12 mb-2">
<a href="@Model.GetMemberProfileUrl(Model.User)" class="text-decoration-none">
<span class="text-dark dot">@Model.User.UserName</span>
</a>
<span class="text-dark-200 dot">@post.CreationTime.ToString("MMMM yyyy")</span>
<span class="text-dark-200">@post.ReadCount.ToString() @L["Views"]</span>
</div>
<h3 class="post-title mb-3">
<a href="@Model.GetBlogPostUrl(post)">
@post.Title
</a>
</h3>
<p class="post-desc">
<a href="@Model.GetBlogPostUrl(post)">
@post.Description.TruncateWithPostfix(150)
</a>
<a href="@Model.GetBlogPostUrl(post)" class="readMore">@L["ReadMore"]</a>
</p>
</div>
<div class="post-img-cont">
<div class="post-list-span text-center post">
<img src="@post.CoverImage" class="box-articles">
</div>
</div>
</div>
}
</div>
</abp-tab>
</abp-tabs>
</div>
}
else
{
<div class="col-md-8">
<div class="mt-5 pt-6">
<p>@L["MemberNotPublishedPostYet"]</p>
</div>
</div>
}
</div>
</div>
</main>

@ -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<PostWithDetailsDto> Posts { get; set; }
public Dictionary<Guid, string> BlogShortNameMap { get; set; }
public IndexModel(IPostAppService postAppService, IMemberAppService memberAppService, IBlogAppService blogAppService)
{
_postAppService = postAppService;
_memberAppService = memberAppService;
_blogAppService = blogAppService;
}
public async Task<IActionResult> 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<Guid, string>();
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;
}
}

@ -0,0 +1,3 @@
.post-desc {
overflow-wrap: break-word;
}

@ -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));
};
})();
})();

Loading…
Cancel
Save