diff --git a/src/main/java/ru/bvn13/voidforum/controllers/PostController.java b/src/main/java/ru/bvn13/voidforum/controllers/PostController.java index fa67bb9..8f99725 100644 --- a/src/main/java/ru/bvn13/voidforum/controllers/PostController.java +++ b/src/main/java/ru/bvn13/voidforum/controllers/PostController.java @@ -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 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+""; + } + } diff --git a/src/main/java/ru/bvn13/voidforum/controllers/account/CommentController.java b/src/main/java/ru/bvn13/voidforum/controllers/account/CommentController.java index bd16665..dd4af0d 100644 --- a/src/main/java/ru/bvn13/voidforum/controllers/account/CommentController.java +++ b/src/main/java/ru/bvn13/voidforum/controllers/account/CommentController.java @@ -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 ""; - } } diff --git a/src/main/java/ru/bvn13/voidforum/controllers/admin/AdminController.java b/src/main/java/ru/bvn13/voidforum/controllers/admin/AdminController.java index 05bd1d3..2e03e6a 100644 --- a/src/main/java/ru/bvn13/voidforum/controllers/admin/AdminController.java +++ b/src/main/java/ru/bvn13/voidforum/controllers/admin/AdminController.java @@ -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."); diff --git a/src/main/java/ru/bvn13/voidforum/forms/CommentForm.java b/src/main/java/ru/bvn13/voidforum/forms/CommentForm.java index 88365f9..ab85889 100644 --- a/src/main/java/ru/bvn13/voidforum/forms/CommentForm.java +++ b/src/main/java/ru/bvn13/voidforum/forms/CommentForm.java @@ -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; } diff --git a/src/main/java/ru/bvn13/voidforum/forms/SettingsForm.java b/src/main/java/ru/bvn13/voidforum/forms/SettingsForm.java index d42a2c2..6f29929 100644 --- a/src/main/java/ru/bvn13/voidforum/forms/SettingsForm.java +++ b/src/main/java/ru/bvn13/voidforum/forms/SettingsForm.java @@ -27,4 +27,7 @@ public class SettingsForm { @NotNull private String mainUri; + @NotNull + private Integer commentsPageSize; + } diff --git a/src/main/java/ru/bvn13/voidforum/repositories/CommentRepository.java b/src/main/java/ru/bvn13/voidforum/repositories/CommentRepository.java index 095214b..0011a78 100644 --- a/src/main/java/ru/bvn13/voidforum/repositories/CommentRepository.java +++ b/src/main/java/ru/bvn13/voidforum/repositories/CommentRepository.java @@ -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 { List findAllByPostOrderById(Post post); List findAllByPostAndParentCommentOrderById(Post post, Comment parentComment); - List findAllByPostAndDeletedMarkOrderById(Post post, Boolean deletedMark); + Page findAllByPostAndDeletedMarkOrderById(Post post, Boolean deletedMark, Pageable pageable); List 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); } diff --git a/src/main/java/ru/bvn13/voidforum/services/AppSetting.java b/src/main/java/ru/bvn13/voidforum/services/AppSetting.java index 09f980b..3d6924a 100644 --- a/src/main/java/ru/bvn13/voidforum/services/AppSetting.java +++ b/src/main/java/ru/bvn13/voidforum/services/AppSetting.java @@ -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); + } } diff --git a/src/main/java/ru/bvn13/voidforum/services/CommentService.java b/src/main/java/ru/bvn13/voidforum/services/CommentService.java index 5687d6d..e6ff2fe 100644 --- a/src/main/java/ru/bvn13/voidforum/services/CommentService.java +++ b/src/main/java/ru/bvn13/voidforum/services/CommentService.java @@ -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 commentComparator = new Comparator() { @Override @@ -37,17 +48,16 @@ public class CommentService { }; - public List getCommentsForPost(Post post) { - List 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 getCommentsForPost(Post post, int page, int pageSize) { + Page 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 filterListByParentComment(List comments, Comment parent) { List 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("
%s
", markdownService.renderToHtml(comment.getContent()))); + } else { + comment.setRenderedContent(String.format("
%s
", comment.getContent())); + } + return commentRepository.save(comment); + } + } diff --git a/src/main/java/ru/bvn13/voidforum/services/UserService.java b/src/main/java/ru/bvn13/voidforum/services/UserService.java index 98b879d..4a413c4 100644 --- a/src/main/java/ru/bvn13/voidforum/services/UserService.java +++ b/src/main/java/ru/bvn13/voidforum/services/UserService.java @@ -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 { diff --git a/src/main/java/ru/bvn13/voidforum/utils/PaginatorUtil.java b/src/main/java/ru/bvn13/voidforum/utils/PaginatorUtil.java new file mode 100644 index 0000000..96532d5 --- /dev/null +++ b/src/main/java/ru/bvn13/voidforum/utils/PaginatorUtil.java @@ -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 createPagesList(Integer from, Integer to, Integer pageSize) { + List 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 createPagesList(Integer from, Integer to) { + List result = new ArrayList<>(); + for (int i=from; i<=to; i++) { + result.add(i); + } + return result; + } + +} diff --git a/src/main/resources/resources/css/comments.css b/src/main/resources/resources/css/comments.css new file mode 100644 index 0000000..9c1ded1 --- /dev/null +++ b/src/main/resources/resources/css/comments.css @@ -0,0 +1,6 @@ + +.comment-form #content-editor { + margin-top: 10px; + height: 200px; + width: 100%; +} \ No newline at end of file diff --git a/src/main/resources/templates/account/posts/edit.jade b/src/main/resources/templates/account/posts/edit.jade index a9b475e..af4c0a5 100644 --- a/src/main/resources/templates/account/posts/edit.jade +++ b/src/main/resources/templates/account/posts/edit.jade @@ -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; + }); }); diff --git a/src/main/resources/templates/admin/home/settings.jade b/src/main/resources/templates/admin/home/settings.jade index 97b94cf..b8c2767 100644 --- a/src/main/resources/templates/admin/home/settings.jade +++ b/src/main/resources/templates/admin/home/settings.jade @@ -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 diff --git a/src/main/resources/templates/admin/posts/new.jade b/src/main/resources/templates/admin/posts/new.jade index e2d205b..d7265fb 100644 --- a/src/main/resources/templates/admin/posts/new.jade +++ b/src/main/resources/templates/admin/posts/new.jade @@ -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 diff --git a/src/main/resources/templates/comments/deleted.jade b/src/main/resources/templates/comments/deleted.jade index f00cb65..490ce78 100644 --- a/src/main/resources/templates/comments/deleted.jade +++ b/src/main/resources/templates/comments/deleted.jade @@ -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 diff --git a/src/main/resources/templates/comments/fragments/commentCreationForm.jade b/src/main/resources/templates/comments/fragments/commentCreationForm.jade index d50cfc0..f9984cb 100644 --- a/src/main/resources/templates/comments/fragments/commentCreationForm.jade +++ b/src/main/resources/templates/comments/fragments/commentCreationForm.jade @@ -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()}') \ No newline at end of file + 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; + }); + }); \ No newline at end of file diff --git a/src/main/resources/templates/comments/list.jade b/src/main/resources/templates/comments/list.jade index 4194458..ddd4391 100644 --- a/src/main/resources/templates/comments/list.jade +++ b/src/main/resources/templates/comments/list.jade @@ -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") ← + | 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") → 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 diff --git a/src/main/resources/templates/comments/one.jade b/src/main/resources/templates/comments/one.jade index 8d06a18..bbf33ae 100644 --- a/src/main/resources/templates/comments/one.jade +++ b/src/main/resources/templates/comments/one.jade @@ -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") diff --git a/src/main/resources/templates/posts/show.jade b/src/main/resources/templates/posts/show.jade index ad609e9..e78c0d8 100644 --- a/src/main/resources/templates/posts/show.jade +++ b/src/main/resources/templates/posts/show.jade @@ -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