implemented ability of commenting posts. implemented comments flow under the post with pagination.

master
Vyacheslav N. Boyko 2017-12-11 16:10:47 +03:00
parent 3186fcc0ea
commit a250ace937
19 changed files with 280 additions and 50 deletions

View File

@ -1,8 +1,14 @@
package ru.bvn13.voidforum.controllers;
import org.springframework.data.domain.Page;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestParam;
import ru.bvn13.voidforum.forms.CommentForm;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.SeoPostData;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.services.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -11,8 +17,11 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.bvn13.voidforum.utils.DTOUtil;
import ru.bvn13.voidforum.utils.PaginatorUtil;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import static org.springframework.web.bind.annotation.RequestMethod.*;
@ -29,6 +38,9 @@ public class PostController {
@Autowired
private PostService postService;
@Autowired
private UserService userService;
@Autowired
private VisitService visitService;
@ -41,6 +53,9 @@ public class PostController {
@Autowired
private RequestProcessorService requestProcessorService;
@Autowired
private AppSetting appSetting;
@RequestMapping(value = "archive", method = GET)
public String archive(Model model){
@ -50,7 +65,7 @@ public class PostController {
}
@RequestMapping(value = "{permalink}", method = GET)
public String show(@PathVariable String permalink, Model model, HttpServletRequest request){
public String show(@PathVariable String permalink, Model model, @RequestParam(defaultValue = "0") int page, HttpServletRequest request){
Post post = this.postService.findPostByPermalink(permalink);
logger.debug(String.format("ACCESS %s from IP: %s", permalink, this.requestProcessorService.getRealIp(request)));
@ -73,12 +88,48 @@ public class PostController {
model.addAttribute("seoDescription", post.getSeoDescription());
model.addAttribute("seoData", seoData);
model.addAttribute("comments", commentService.getCommentsForPost(post));
model.addAttribute("commentForm", new CommentForm());
CommentForm commentForm = new CommentForm();
commentForm.setPostId(post.getId());
Integer commentsPageSize = appSetting.getCommentsPageSize();
Integer lastPage = commentService.getLastPageCommentsForPost(post, commentsPageSize);
Page<Comment> comments = null;
if (page > 0) {
comments = commentService.getCommentsForPost(post, page - 1, commentsPageSize);
} else {
page = lastPage+1;
comments = commentService.getCommentsForPost(post, lastPage, commentsPageSize);
}
model.addAttribute("page", page);
model.addAttribute("pagesList", PaginatorUtil.createPagesList(1, lastPage+1));
model.addAttribute("totalPages", comments.getTotalPages());
model.addAttribute("comments", comments);
model.addAttribute("commentForm", commentForm);
model.addAttribute("commentFormats", commentService.getAvailableCommentFormats());
return "posts/show";
}
@RequestMapping(value = "{permalink}/comments", method = POST)
public String addComment(@PathVariable String permalink, @Valid CommentForm commentForm, Errors errors, Model model) {
User user = userService.currentUser();
if (!userService.currentUserHasPrivilege(PrivilegeService.PRIVILEGE_WRITE)) {
throw new AccessDeniedException("You are not allowed here");
}
Comment comment = new Comment();
DTOUtil.mapTo(commentForm, comment);
comment.setUser(user);
comment.setPost(postService.getPost(commentForm.getPostId()));
comment.setParentComment(commentService.getCommentById(commentForm.getParentCommentId()));
comment.setDepth(commentService.calculateDepth(comment));
commentService.saveComment(comment);
return "redirect:/posts/"+permalink+"";
}
}

View File

@ -1,8 +1,19 @@
package ru.bvn13.voidforum.controllers.account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestMapping;
import ru.bvn13.voidforum.forms.CommentForm;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.repositories.CommentRepository;
import ru.bvn13.voidforum.services.CommentService;
import ru.bvn13.voidforum.services.PostService;
import ru.bvn13.voidforum.services.UserService;
import ru.bvn13.voidforum.utils.DTOUtil;
import javax.validation.Valid;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@ -13,11 +24,15 @@ import static org.springframework.web.bind.annotation.RequestMethod.POST;
@RequestMapping("/account/comments")
public class CommentController {
@Autowired
private UserService userService;
@Autowired
private PostService postService;
@Autowired
private CommentService commentService;
@RequestMapping(value = "", method = POST)
public String addComment(Model model) {
return "";
}
}

View File

@ -51,6 +51,7 @@ public class AdminController {
appSetting.setPageSize(settingsForm.getPageSize());
appSetting.setStoragePath(settingsForm.getStoragePath());
appSetting.setMainUri(settingsForm.getMainUri());
appSetting.setCommentsPageSize(settingsForm.getCommentsPageSize());
MessageHelper.addSuccessAttribute(ra, "Update settings successfully.");

View File

@ -16,14 +16,17 @@ import javax.validation.constraints.NotNull;
public class CommentForm {
@NotNull
private Integer postId;
private Long postId;
private Integer parentCommentId;
private Long parentCommentId;
@NotEmpty
private String content;
@NotNull
private CommentFormat commentFormat;
private CommentFormat commentFormat = CommentFormat.MARKDOWN;
@NotNull
private Boolean deletedMark = false;
}

View File

@ -27,4 +27,7 @@ public class SettingsForm {
@NotNull
private String mainUri;
@NotNull
private Integer commentsPageSize;
}

View File

@ -1,6 +1,10 @@
package ru.bvn13.voidforum.repositories;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
@ -12,7 +16,9 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {
List<Comment> findAllByPostOrderById(Post post);
List<Comment> findAllByPostAndParentCommentOrderById(Post post, Comment parentComment);
List<Comment> findAllByPostAndDeletedMarkOrderById(Post post, Boolean deletedMark);
Page<Comment> findAllByPostAndDeletedMarkOrderById(Post post, Boolean deletedMark, Pageable pageable);
List<Comment> findAllByPostAndParentCommentAndDeletedMarkOrderById(Post post, Comment parentComment, Boolean deletedMark);
@Query("SELECT COUNT(c.id) FROM Comment AS c WHERE c.post = :post AND c.deletedMark = :deletedMark")
Integer getCommentsCountByPostAndDeletedMark(@Param("post") Post post, @Param("deletedMark") Boolean deletedMark);
}

View File

@ -21,12 +21,14 @@ public class AppSetting {
private Integer pageSize = 5;
private String storagePath = "/tmp";
private String mainUri = "http://localhost/";
private Integer commentsPageSize = 50;
public static final String SITE_NAME = "site_name";
public static final String SITE_SLOGAN = "site_slogan";
public static final String PAGE_SIZE = "page_size";
public static final String STORAGE_PATH = "storage_path";
public static final String MAIN_URI = "main_uri";
public static final String COMMENTS_PAGE_SIZE = "comments_page_size";
@Autowired
public AppSetting(SettingService settingService){
@ -90,4 +92,13 @@ public class AppSetting {
ogTypes.add("article");
return ogTypes;
}
public Integer getCommentsPageSize() {
return (Integer) settingService.get(COMMENTS_PAGE_SIZE, pageSize);
}
public void setCommentsPageSize(Integer commentsPageSize) {
this.commentsPageSize = commentsPageSize;
settingService.put(COMMENTS_PAGE_SIZE, commentsPageSize);
}
}

View File

@ -2,12 +2,16 @@ package ru.bvn13.voidforum.services;
import com.domingosuarez.boot.autoconfigure.jade4j.JadeHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import ru.bvn13.voidforum.models.Comment;
import ru.bvn13.voidforum.models.Post;
import ru.bvn13.voidforum.models.User;
import ru.bvn13.voidforum.models.support.CommentFormat;
import ru.bvn13.voidforum.repositories.CommentRepository;
import ru.bvn13.voidforum.support.web.MarkdownService;
import java.util.ArrayList;
import java.util.Collections;
@ -28,6 +32,13 @@ public class CommentService {
@Autowired
private CommentRepository commentRepository;
@Autowired
private MarkdownService markdownService;
public Comment getCommentById(Long commentId) {
return commentRepository.findOne(commentId);
}
private static Comparator<Comment> commentComparator = new Comparator<Comment>() {
@Override
@ -37,17 +48,16 @@ public class CommentService {
};
public List<Comment> getCommentsForPost(Post post) {
List<Comment> availableComments = null;
//User user = userService.currentUser();
//if (userService.hasPrivilege(user, PrivilegeService.PRIVILEGE_WRITE)) {
availableComments = commentRepository.findAllByPostOrderById(post);
//} else {
// availableComments = commentRepository.findAllByPostAndDeletedMarkOrderById(post, false);
//}
public Page<Comment> getCommentsForPost(Post post, int page, int pageSize) {
Page<Comment> availableComments = commentRepository.findAllByPostAndDeletedMarkOrderById(post, false, new PageRequest(page, pageSize, Sort.Direction.ASC, "createdAt"));
return availableComments;
}
public Integer getLastPageCommentsForPost(Post post, int pageSize) {
Integer count = commentRepository.getCommentsCountByPostAndDeletedMark(post, false);
return (int) Math.ceil(count.intValue() / pageSize);
}
public List<Comment> filterListByParentComment(List<Comment> comments, Comment parent) {
List<Comment> children = new ArrayList<>();
comments.forEach(c -> {
@ -85,4 +95,20 @@ public class CommentService {
return formats;
}
public Integer calculateDepth(Comment comment) {
if (comment.getParentComment() == null) {
return 0;
}
return 1 + calculateDepth(comment.getParentComment());
}
public Comment saveComment(Comment comment) {
if (comment.getCommentFormat() == CommentFormat.MARKDOWN) {
comment.setRenderedContent(String.format("<div class=\"markdown-post\">%s</div>", markdownService.renderToHtml(comment.getContent())));
} else {
comment.setRenderedContent(String.format("<div class=\"html-post\">%s</div>", comment.getContent()));
}
return commentRepository.save(comment);
}
}

View File

@ -77,7 +77,7 @@ public class UserService implements UserDetailsService {
User user = userRepository.findByEmail(email);
if (user == null) {
springUser = new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
"", "", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName(RoleService.ROLE_VISITOR))
)
@ -162,6 +162,10 @@ public class UserService implements UserDetailsService {
return this.hasPrivilege(user, privilege);
}
public Boolean currentUserCanWrite() {
return this.currentUserHasPrivilege(PrivilegeService.PRIVILEGE_WRITE);
}
public User registerNewUserAccount(User user) throws EmailExistsException, NicknameExistsException {

View File

@ -0,0 +1,28 @@
package ru.bvn13.voidforum.utils;
import java.util.ArrayList;
import java.util.List;
/**
* Created by bvn13 on 11.12.2017.
*/
public class PaginatorUtil {
public static List<Integer> createPagesList(Integer from, Integer to, Integer pageSize) {
List<Integer> result = new ArrayList<>();
Integer lastPage = (int) Math.ceil(to / pageSize);
for (int i=from; i<=lastPage; i++) {
result.add(i);
}
return result;
}
public static List<Integer> createPagesList(Integer from, Integer to) {
List<Integer> result = new ArrayList<>();
for (int i=from; i<=to; i++) {
result.add(i);
}
return result;
}
}

View File

@ -0,0 +1,6 @@
.comment-form #content-editor {
margin-top: 10px;
height: 200px;
width: 100%;
}

View File

@ -71,17 +71,19 @@ block content
script
:javascript
var editor = ace.edit("content-editor");
editor.setTheme("ace/theme/github");
$(document).ready(function() {
var editor = ace.edit("content-editor");
editor.setTheme("ace/theme/github");
var MarkdownMode = ace.require("ace/mode/markdown").Mode;
editor.getSession().setMode(new MarkdownMode());
var MarkdownMode = ace.require("ace/mode/markdown").Mode;
editor.getSession().setMode(new MarkdownMode());
editor.getSession().setUseWrapMode(true);
editor.getSession().setUseWrapMode(true);
$("form").submit(function(){
$("#content").val(editor.getValue());
return true;
$("form").submit(function(){
$("#content").val(editor.getValue());
return true;
});
});

View File

@ -21,6 +21,10 @@ block content
td.setting-label Page Size
td.input
input.form-control(type="text", name="pageSize", value="#{settings.getPageSize()}")
tr
td.setting-label Comments page Size
td.input
input.form-control(type="text", name="commentsPageSize", value="#{settings.getCommentsPageSize()}")
tr
td.setting-label Storage path
td.input

View File

@ -25,7 +25,7 @@ block content
textarea.form-control#content(name="content", style="display:none;")
= postForm.getContent()
div#content-editor
#{postForm.getContent()}
= postForm.getContent()
.item-row
hr
.row

View File

@ -1,9 +1,7 @@
// Created by bvn13 on 09.12.2017.
div
.tr.comment-deleted
.colgroup
| Sometimes there was a comment here...
.panel.panel-warning
.panel-body
| Sometimes there was a comment here...
//if commentService.childrenCount(comments, comment) > 0

View File

@ -1,8 +1,46 @@
// Created by bvn13 on 09.12.2017.
form.post-form(action="/account/comments", method="POST")
form#commentForm.comment-form(action="comments", method="POST")
.item-row
input(type="hidden", name='_csrf', value='#{_csrf.token}')
.item-row
input(type="hidden", name='parentComment', value='#{comment.getParentComment().getId()}')
input(type="hidden", name='postId', value='#{commentForm.getPostId()}')
.item-row
input(type="hidden", name='parentCommentId', value='#{commentForm.getParentCommentId()}')
.row
.col-sm-3
span Format
select.form-control(name="commentFormat")
for format in commentFormats
if format != commentForm.getCommentFormat()
option(value="#{format.getId()}") #{format.getName()}
else
option(value="#{format.getId()}", selected="selected") #{format.getName()}
.item-row
textarea.form-control#content(name="content", style="display:none;")
= commentForm.getContent()
div#content-editor
= commentForm.getContent()
.item-row
input.btn.btnSuccess(type="submit", value="Add comment")
script
:javascript
$(document).ready(function() {
var editor = ace.edit("content-editor");
editor.setTheme("ace/theme/github");
var MarkdownMode = ace.require("ace/mode/markdown").Mode;
editor.getSession().setMode(new MarkdownMode());
editor.getSession().setUseWrapMode(true);
$("#commentForm").submit(function () {
$("#content").val(editor.getValue());
return true;
});
});

View File

@ -1,4 +1,29 @@
// Created by bvn13 on 09.12.2017.
.load-more
nav
ul.pagination
if page > 1
li.previous
a.btn(href="?page=#{page - 1}")
span(aria-hidden="true") &larr;
| Older comments
for pageNum in pagesList
if pageNum==page
li.active
a.btn(href="?page=#{pageNum}")
= pageNum
else
li
a.btn(href="?page=#{pageNum}")
= pageNum
if totalPages > page
li.next
a.btn(href="?page=#{page + 1}")
| Newer comments
span(aria-hidden="true") &rarr;
for comment in comments
if comment.getDeletedMark()
@ -7,4 +32,5 @@ for comment in comments
include one
include fragments/commentCreationForm
if userService.currentUserCanWrite()
include fragments/commentCreationForm

View File

@ -1,19 +1,20 @@
// Created by bvn13 on 09.12.2017.
div
.table
.tr.comment
.td
= comment.getUser().getNickname()
= viewHelper.getFormattedDate(comment.getCreatedAt())
.td
!{comment.getRenderedContext()}
.panel.panel-default
.panel-heading
span #{comment.getUser().getNickname()}, #{viewHelper.getFormattedDate(comment.getCreatedAt())}
div
if userService.isCurrentUserAdmin()
.td
admin
.panel-body
!{comment.getRenderedContent()}
include fragments/commentCreationForm
//if userService.currentUserCanWrite()
// div
// = userService.currentUser().getNickname()
// include fragments/commentCreationForm
//if commentService.childrenCount(comments, comment) > 0
// .comment-children(style="padding-left: #{5 * commentService.childrenCount(comments, comment)}px")

View File

@ -2,7 +2,14 @@ extends ../layout/app
block title
= viewHelper.metaTitle(post.getTitle())
if userService.currentUserCanWrite()
block head
link(rel='stylesheet', type='text/css', href='/css/comments.css')
script(src="/webjars/ace/1.2.8/src-noconflict/ace.js")
script(src="/webjars/ace/1.2.8/src-noconflict/theme-github.js")
script(src="/webjars/ace/1.2.8/src-noconflict/mode-markdown.js")
block page_title
h1 #{post.getTitle()}
include ../posts/fragments/social