Browse Source

Post Edit & create

pull/318/head v0.3.1
Yunus Emre Kalkan 8 years ago
parent
commit
f62ed01ac2
  1. 2
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/IBlogAppService.cs
  2. 16
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs
  3. 16
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/GetPostForEditOutput.cs
  4. 10
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs
  5. 4
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostDto.cs
  6. 16
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs
  7. 16
      modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/UpdatePostDto.cs
  8. 2
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs
  9. 5
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationModule.cs
  10. 11
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Blogs/BlogAppService.cs
  11. 44
      modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs
  12. 2
      modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs
  13. 6
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml
  14. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs
  15. 31
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml
  16. 49
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs
  17. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml
  18. 4
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs
  19. 30
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml
  20. 52
      modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs
  21. 10
      modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj
  22. 83
      modules/blogging/test/Volo.Blogging.Application.Tests/Volo/Blogging/PostAppService_Tests.cs

2
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Blogs/IBlogAppService.cs

@ -12,5 +12,7 @@ namespace Volo.Blogging.Blogs
Task<ListResultDto<BlogDto>> GetListAsync(); Task<ListResultDto<BlogDto>> GetListAsync();
Task<BlogDto> GetByShortNameAsync(string shortName); Task<BlogDto> GetByShortNameAsync(string shortName);
Task<BlogDto> GetAsync(Guid id);
} }
} }

16
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/CreatePostDto.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class CreatePostDto
{
public Guid BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
}

16
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/GetPostForEditOutput.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class GetPostForEditOutput : FullAuditedEntityDto<Guid>
{
public Guid BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
}

10
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/IPostAppService.cs

@ -9,8 +9,14 @@ namespace Volo.Blogging.Posts
{ {
public interface IPostAppService : IApplicationService public interface IPostAppService : IApplicationService
{ {
ListResultDto<PostDto> GetPostsByBlogId(Guid id); ListResultDto<PostWithDetailsDto> GetListByBlogIdAsync(Guid id);
Task<PostDto> GetPost(GetPostInput input); Task<PostWithDetailsDto> GetByTitleAsync(GetPostInput input);
Task<PostWithDetailsDto> GetAsync(Guid id);
Task<PostWithDetailsDto> CreateAsync(CreatePostDto input);
Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input);
} }
} }

4
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostDto.cs

@ -3,14 +3,12 @@ using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts namespace Volo.Blogging.Posts
{ {
public class PostDto : EntityDto<Guid> public class PostDto : FullAuditedEntityDto<Guid>
{ {
public Guid BlogId { get; protected set; } public Guid BlogId { get; protected set; }
public string Title { get; protected set; } public string Title { get; protected set; }
public string Content { get; set; } public string Content { get; set; }
public DateTime CreationTime { get; set; }
} }
} }

16
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/PostWithDetailsDto.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class PostWithDetailsDto : FullAuditedEntityDto<Guid>
{
public Guid BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
}

16
modules/blogging/src/Volo.Blogging.Application.Contracts/Volo/Blogging/Posts/UpdatePostDto.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp.Application.Dtos;
namespace Volo.Blogging.Posts
{
public class UpdatePostDto
{
public Guid BlogId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
}

2
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationAutoMapperProfile.cs

@ -1,4 +1,5 @@
using AutoMapper; using AutoMapper;
using Volo.Abp.AutoMapper;
using Volo.Blogging.Blogs; using Volo.Blogging.Blogs;
using Volo.Blogging.Posts; using Volo.Blogging.Posts;
@ -10,6 +11,7 @@ namespace Volo.Blogging
{ {
CreateMap<Blog, BlogDto>(); CreateMap<Blog, BlogDto>();
CreateMap<Post, PostDto>(); CreateMap<Post, PostDto>();
CreateMap<Post, PostWithDetailsDto>();
} }
} }
} }

5
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/BloggingApplicationModule.cs

@ -14,6 +14,11 @@ namespace Volo.Blogging
{ {
public override void ConfigureServices(IServiceCollection services) public override void ConfigureServices(IServiceCollection services)
{ {
services.Configure<AbpAutoMapperOptions>(options =>
{
options.AddProfile<BloggingApplicationAutoMapperProfile>(validate: true);
});
services.AddAssemblyOf<BloggingApplicationModule>(); services.AddAssemblyOf<BloggingApplicationModule>();
} }
} }

11
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Blogs/BlogAppService.cs

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
@ -34,6 +35,12 @@ namespace Volo.Blogging.Blogs
return ObjectMapper.Map<Blog, BlogDto>(blog); return ObjectMapper.Map<Blog, BlogDto>(blog);
} }
public async Task<BlogDto> GetAsync(Guid id)
{
var blog = await _blogRepository.GetAsync(id);
return ObjectMapper.Map<Blog, BlogDto>(blog);
}
} }
} }

44
modules/blogging/src/Volo.Blogging.Application/Volo/Blogging/Posts/PostAppService.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using System.Collections.Generic; using System.Collections.Generic;
using Volo.Abp.Users;
namespace Volo.Blogging.Posts namespace Volo.Blogging.Posts
{ {
@ -16,19 +17,52 @@ namespace Volo.Blogging.Posts
_postRepository = postRepository; _postRepository = postRepository;
} }
public ListResultDto<PostDto> GetPostsByBlogId(Guid id) public ListResultDto<PostWithDetailsDto> GetListByBlogIdAsync(Guid id)
{ {
var posts = _postRepository.GetPostsByBlogId(id); var posts = _postRepository.GetPostsByBlogId(id);
return new ListResultDto<PostDto>( return new ListResultDto<PostWithDetailsDto>(
ObjectMapper.Map<List<Post>, List<PostDto>>(posts)); ObjectMapper.Map<List<Post>, List<PostWithDetailsDto>>(posts));
} }
public async Task<PostDto> GetPost(GetPostInput input) public async Task<PostWithDetailsDto> GetByTitleAsync(GetPostInput input)
{ {
var post = await _postRepository.GetPost(input.BlogId, input.Title); var post = await _postRepository.GetPost(input.BlogId, input.Title);
return ObjectMapper.Map<Post,PostDto>(post); return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
public async Task<PostWithDetailsDto> GetAsync(Guid id)
{
var post = await _postRepository.GetAsync(id);
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
public async Task<PostWithDetailsDto> UpdateAsync(Guid id, UpdatePostDto input)
{
var post = await _postRepository.GetAsync(id);
post.SetTitle(input.Title);
post.Content = input.Content;
post = await _postRepository.UpdateAsync(post);
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
}
public async Task<PostWithDetailsDto> CreateAsync(CreatePostDto input)
{
var post = new Post(
GuidGenerator.Create(),
blogId: input.BlogId,
creatorId: CurrentUser.GetId(),
title: input.Title
) {Content = input.Content};
await _postRepository.InsertAsync(post);
return ObjectMapper.Map<Post, PostWithDetailsDto>(post);
} }
} }
} }

2
modules/blogging/src/Volo.Blogging.Web/BloggingWebModule.cs

@ -35,6 +35,8 @@ namespace Volo.Blogging
//TODO: Make configurable! //TODO: Make configurable!
options.Conventions.AddPageRoute("/Blog/Posts/Index", "blog/{blogShortName}"); options.Conventions.AddPageRoute("/Blog/Posts/Index", "blog/{blogShortName}");
options.Conventions.AddPageRoute("/Blog/Posts/Detail", "blog/{blogShortName}/{postTitle}"); options.Conventions.AddPageRoute("/Blog/Posts/Detail", "blog/{blogShortName}/{postTitle}");
options.Conventions.AddPageRoute("/Blog/Posts/Edit", "blog/{blogShortName}/manage/{postId}");
options.Conventions.AddPageRoute("/Blog/Posts/New", "blog/{blogShortName}/manage/new");
}); });
services.AddAssemblyOf<BloggingWebModule>(); services.AddAssemblyOf<BloggingWebModule>();

6
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml

@ -8,4 +8,8 @@
<p>@Model.Post.Content</p> <p>@Model.Post.Content</p>
<i>Posted to @Model.Blog.Name On @Model.Post.CreationTime</i> <p>
<i>Posted to @Model.Blog.Name On @Model.Post.CreationTime</i>
</p>
<a asp-page="./Edit" asp-route-postId="@Model.Post.Id" asp-route-blogShortName="@Model.BlogShortName">edit</a>

4
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Detail.cshtml.cs

@ -20,7 +20,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string PostTitle { get; set; } public string PostTitle { get; set; }
public PostDto Post { get; set; } public PostWithDetailsDto Post { get; set; }
public BlogDto Blog { get; set; } public BlogDto Blog { get; set; }
@ -34,7 +34,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName); var blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetPost(new GetPostInput {BlogId = blog.Id , Title = PostTitle}); Post = await _postAppService.GetByTitleAsync(new GetPostInput {BlogId = blog.Id , Title = PostTitle});
Blog = blog; Blog = blog;
} }
} }

31
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml

@ -0,0 +1,31 @@
@page
@using Volo.Blogging.Pages.Blog.Posts
@model EditModel
@{
}
@using (Html.BeginForm(FormMethod.Post))
{
<div class="form-group">
<label class="col-sm-2 control-label">Title</label>
<div class="col-sm-10">
<input class="form-control" name="Title" value="@Model.Post.Title">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Content</label>
<div class="col-sm-10">
<textarea rows="4" class="form-control" name="Content">@Model.Post.Content</textarea>
</div>
</div>
<input name="BlogId" value="@Model.Post.BlogId" hidden="">
<input name="Id" value="@Model.Post.Id" hidden="">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Save</button>
</div>
</div>
}

49
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Edit.cshtml.cs

@ -0,0 +1,49 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.Application.Dtos;
using Volo.Blogging.Blogs;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Blog.Posts
{
public class EditModel : PageModel
{
private readonly IPostAppService _postAppService;
private readonly IBlogAppService _blogAppService;
[BindProperty(SupportsGet = true)]
public string BlogShortName { get; set; }
[BindProperty(SupportsGet = true)]
public string PostId { get; set; }
public PostWithDetailsDto Post { get; set; }
public BlogDto Blog { get; set; }
public EditModel(IPostAppService postAppService, IBlogAppService blogAppService)
{
_postAppService = postAppService;
_blogAppService = blogAppService;
}
public async void OnGet()
{
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = await _postAppService.GetAsync(new Guid(PostId));
Blog = blog;
}
public async Task<ActionResult> OnPost(Guid id, UpdatePostDto post)
{
var editedPost = await _postAppService.UpdateAsync(id, post);
var blog = await _blogAppService.GetAsync(editedPost.BlogId);
return Redirect(Url.Content($"~/blog/{blog.ShortName}/{editedPost.Title}"));
}
}
}

4
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml

@ -10,6 +10,8 @@
<ul> <ul>
@foreach (var post in Model.Posts) @foreach (var post in Model.Posts)
{ {
<li><a asp-page="./Detail" asp-route-postTitle="@post.Title" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a></li> <li><a asp-page="./Detail" asp-route-postTitle="@post.Title" asp-route-blogShortName="@Model.BlogShortName">@post.Title</a></li>
} }
</ul> </ul>
<a asp-page="./New" asp-route-blogShortName="@Model.BlogShortName">Create New Post</a>

4
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/Index.cshtml.cs

@ -19,7 +19,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
[BindProperty(SupportsGet = true)] [BindProperty(SupportsGet = true)]
public string BlogShortName { get; set; } public string BlogShortName { get; set; }
public IReadOnlyList<PostDto> Posts { get; set; } public IReadOnlyList<PostWithDetailsDto> Posts { get; set; }
public IndexModel(IPostAppService postAppService, IBlogAppService blogAppService) public IndexModel(IPostAppService postAppService, IBlogAppService blogAppService)
{ {
@ -31,7 +31,7 @@ namespace Volo.Blogging.Pages.Blog.Posts
{ {
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName); var blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Posts = _postAppService.GetPostsByBlogId(blog.Id).Items; Posts = _postAppService.GetListByBlogIdAsync(blog.Id).Items;
} }
} }
} }

30
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml

@ -0,0 +1,30 @@
@page
@using Volo.Blogging.Pages.Blog.Posts
@model NewModel
@{
}
@using (Html.BeginForm(FormMethod.Post))
{
<div class="form-group">
<label class="col-sm-2 control-label">Title</label>
<div class="col-sm-10">
<input class="form-control" name="Title" value="@Model.Post.Title">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Content</label>
<div class="col-sm-10">
<textarea rows="4" class="form-control" name="Content">@Model.Post.Content</textarea>
</div>
</div>
<input name="BlogId" value="@Model.Post.BlogId" hidden="">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Save</button>
</div>
</div>
}

52
modules/blogging/src/Volo.Blogging.Web/Pages/Blog/Posts/New.cshtml.cs

@ -0,0 +1,52 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Volo.Abp.Application.Dtos;
using Volo.Blogging.Blogs;
using Volo.Blogging.Posts;
namespace Volo.Blogging.Pages.Blog.Posts
{
public class NewModel : PageModel
{
private readonly IPostAppService _postAppService;
private readonly IBlogAppService _blogAppService;
[BindProperty(SupportsGet = true)]
public string BlogShortName { get; set; }
[BindProperty(SupportsGet = true)]
public string PostId { get; set; }
public PostWithDetailsDto Post { get; set; }
public BlogDto Blog { get; set; }
public NewModel(IPostAppService postAppService, IBlogAppService blogAppService)
{
_postAppService = postAppService;
_blogAppService = blogAppService;
}
public async void OnGet()
{
var blog = await _blogAppService.GetByShortNameAsync(BlogShortName);
Post = new PostWithDetailsDto()
{
BlogId = blog.Id
};
Blog = blog;
}
public async Task<ActionResult> OnPost(CreatePostDto post)
{
var insertedPost = await _postAppService.CreateAsync(post);
var blog = await _blogAppService.GetAsync(insertedPost.BlogId);
return Redirect(Url.Content($"~/blog/{blog.ShortName}/{insertedPost.Title}"));
}
}
}

10
modules/blogging/src/Volo.Blogging.Web/Volo.Blogging.Web.csproj

@ -24,4 +24,14 @@
<Content Remove="Pages\Blog\Posts\Index.cshtml" /> <Content Remove="Pages\Blog\Posts\Index.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Remove="Pages\Blog\Posts\Edit.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Update="Pages\Blog\Posts\Edit.cshtml">
<Pack>$(IncludeRazorContentInPack)</Pack>
</Content>
</ItemGroup>
</Project> </Project>

83
modules/blogging/test/Volo.Blogging.Application.Tests/Volo/Blogging/PostAppService_Tests.cs

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Shouldly;
using Volo.Blogging.Blogs;
using Volo.Blogging.Posts;
using Xunit;
namespace Volo.Blogging
{
public class PostAppService_Tests : BloggingApplicationTestBase
{
private readonly IPostAppService _postAppService;
private readonly IBlogRepository _blogRepository;
private readonly BloggingTestData _testData;
public PostAppService_Tests()
{
_testData = GetRequiredService<BloggingTestData>();
_postAppService = GetRequiredService<IPostAppService>();
_blogRepository = GetRequiredService<IBlogRepository>();
}
[Fact]
public async Task Should_Create_A_Post()
{
var blogId = (await _blogRepository.GetListAsync()).First().Id;
var title = "title";
var content = "content";
var newPost = await _postAppService.CreateAsync(new CreatePostDto()
{
BlogId = blogId,
Title = title,
Content = content
});
UsingDbContext(context =>
{
var post = context.Posts.FirstOrDefault(q => q.Title == title);
post.ShouldNotBeNull();
post.Title.ShouldBe(title);
post.Content.ShouldBe(content);
post.BlogId.ShouldBe(blogId);
});
}
[Fact]
public async Task Should_Create_And_Update_A_Post()
{
var blogId = (await _blogRepository.GetListAsync()).First().Id;
var title = "title";
var newTitle = "newtitle";
var content = "content";
var newPost = await _postAppService.CreateAsync(new CreatePostDto()
{
BlogId = blogId,
Title = title,
Content = content
});
await _postAppService.UpdateAsync(newPost.Id, new UpdatePostDto()
{
BlogId = blogId,
Title = newTitle,
Content = content
});
UsingDbContext(context =>
{
var post = context.Posts.FirstOrDefault(q => q.Id == newPost.Id);
post.ShouldNotBeNull();
post.Title.ShouldBe(newTitle);
post.Content.ShouldBe(content);
post.BlogId.ShouldBe(blogId);
});
}
}
}
Loading…
Cancel
Save