From 1b2aa3c3e5021f50e0f594ec524295c2da96d2d8 Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Tue, 1 Mar 2022 17:11:01 +0800 Subject: [PATCH 1/5] refactor: Add assembler for post and sheet to replace service convert --- .../controller/admin/api/PostController.java | 23 +- .../controller/admin/api/SheetController.java | 21 +- .../content/ContentContentController.java | 7 +- .../content/ContentFeedController.java | 16 +- .../content/ContentSearchController.java | 9 +- .../content/api/CategoryController.java | 10 +- .../content/api/PostController.java | 53 +- .../content/api/SheetController.java | 18 +- .../controller/content/api/TagController.java | 10 +- .../content/auth/CategoryAuthentication.java | 2 +- .../content/auth/PostAuthentication.java | 16 +- .../content/model/CategoryModel.java | 12 +- .../controller/content/model/PostModel.java | 42 +- .../controller/content/model/SheetModel.java | 23 +- .../controller/content/model/TagModel.java | 15 +- .../core/freemarker/tag/PostTagDirective.java | 28 +- .../comment/CommentEventListener.java | 20 +- .../run/halo/app/service/PostService.java | 72 --- .../run/halo/app/service/SheetService.java | 23 +- .../service/assembler/BasePostAssembler.java | 188 ++++++++ .../app/service/assembler/PostAssembler.java | 453 ++++++++++++++++++ .../assembler/PostRenderAssembler.java | 82 ++++ .../app/service/assembler/SheetAssembler.java | 185 +++++++ .../assembler/SheetRenderAssembler.java | 68 +++ .../app/service/base/BasePostService.java | 66 --- .../app/service/impl/BasePostServiceImpl.java | 75 --- .../app/service/impl/PostServiceImpl.java | 381 +-------------- .../app/service/impl/SheetServiceImpl.java | 123 ----- .../app/service/impl/CategoryServiceTest.java | 37 +- 29 files changed, 1222 insertions(+), 856 deletions(-) create mode 100644 src/main/java/run/halo/app/service/assembler/BasePostAssembler.java create mode 100644 src/main/java/run/halo/app/service/assembler/PostAssembler.java create mode 100644 src/main/java/run/halo/app/service/assembler/PostRenderAssembler.java create mode 100644 src/main/java/run/halo/app/service/assembler/SheetAssembler.java create mode 100644 src/main/java/run/halo/app/service/assembler/SheetRenderAssembler.java diff --git a/src/main/java/run/halo/app/controller/admin/api/PostController.java b/src/main/java/run/halo/app/controller/admin/api/PostController.java index db3b8eb09b..9b6eae9c3e 100644 --- a/src/main/java/run/halo/app/controller/admin/api/PostController.java +++ b/src/main/java/run/halo/app/controller/admin/api/PostController.java @@ -35,6 +35,7 @@ import run.halo.app.model.vo.PostDetailVO; import run.halo.app.service.OptionService; import run.halo.app.service.PostService; +import run.halo.app.service.assembler.PostAssembler; import run.halo.app.utils.HaloUtils; /** @@ -55,12 +56,16 @@ public class PostController { private final OptionService optionService; + private final PostAssembler postAssembler; + public PostController(PostService postService, AbstractStringCacheStore cacheStore, - OptionService optionService) { + OptionService optionService, + PostAssembler postAssembler) { this.postService = postService; this.cacheStore = cacheStore; this.optionService = optionService; + this.postAssembler = postAssembler; } @GetMapping @@ -71,17 +76,17 @@ public Page pageBy( @RequestParam(value = "more", defaultValue = "true") Boolean more) { Page postPage = postService.pageBy(postQuery, pageable); if (more) { - return postService.convertToListVo(postPage, true); + return postAssembler.convertToListVo(postPage); } - return postService.convertToSimple(postPage); + return postAssembler.convertToSimple(postPage); } @GetMapping("latest") @ApiOperation("Pages latest post") public List pageLatest( @RequestParam(name = "top", defaultValue = "10") int top) { - return postService.convertToMinimal(postService.pageLatest(top).getContent()); + return postAssembler.convertToMinimal(postService.pageLatest(top).getContent()); } @GetMapping("status/{status}") @@ -93,17 +98,17 @@ public Page pageByStatus( Page posts = postService.pageBy(status, pageable); if (more) { - return postService.convertToListVo(posts, true); + return postAssembler.convertToListVo(posts); } - return postService.convertToSimple(posts); + return postAssembler.convertToSimple(posts); } @GetMapping("{postId:\\d+}") @ApiOperation("Gets a post") public PostDetailVO getBy(@PathVariable("postId") Integer postId) { Post post = postService.getWithLatestContentById(postId); - return postService.convertToDetailVo(post, true); + return postAssembler.convertToDetailVo(post); } @PutMapping("{postId:\\d+}/likes") @@ -164,7 +169,7 @@ public BasePostDetailDTO updateDraftBy( // Update draft content Post post = postService.updateDraftContent(formattedContent, contentParam.getOriginalContent(), postId); - return postService.convertToDetail(post); + return postAssembler.convertToDetail(post); } @DeleteMapping("{postId:\\d+}") @@ -187,7 +192,7 @@ public String preview(@PathVariable("postId") Integer postId) post.setSlug(URLEncoder.encode(post.getSlug(), StandardCharsets.UTF_8.name())); - BasePostMinimalDTO postMinimalDTO = postService.convertToMinimal(post); + BasePostMinimalDTO postMinimalDTO = postAssembler.convertToMinimal(post); String token = HaloUtils.simpleUUID(); diff --git a/src/main/java/run/halo/app/controller/admin/api/SheetController.java b/src/main/java/run/halo/app/controller/admin/api/SheetController.java index 83a7d43b88..9aa89a005e 100644 --- a/src/main/java/run/halo/app/controller/admin/api/SheetController.java +++ b/src/main/java/run/halo/app/controller/admin/api/SheetController.java @@ -33,6 +33,7 @@ import run.halo.app.model.vo.SheetListVO; import run.halo.app.service.OptionService; import run.halo.app.service.SheetService; +import run.halo.app.service.assembler.SheetAssembler; import run.halo.app.utils.HaloUtils; /** @@ -52,19 +53,23 @@ public class SheetController { private final OptionService optionService; + private final SheetAssembler sheetAssembler; + public SheetController(SheetService sheetService, AbstractStringCacheStore cacheStore, - OptionService optionService) { + OptionService optionService, + SheetAssembler sheetAssembler) { this.sheetService = sheetService; this.cacheStore = cacheStore; this.optionService = optionService; + this.sheetAssembler = sheetAssembler; } @GetMapping("{sheetId:\\d+}") @ApiOperation("Gets a sheet") public SheetDetailVO getBy(@PathVariable("sheetId") Integer sheetId) { Sheet sheet = sheetService.getWithLatestContentById(sheetId); - return sheetService.convertToDetailVo(sheet); + return sheetAssembler.convertToDetailVo(sheet); } @GetMapping @@ -72,7 +77,7 @@ public SheetDetailVO getBy(@PathVariable("sheetId") Integer sheetId) { public Page pageBy( @PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) { Page sheetPage = sheetService.pageBy(pageable); - return sheetService.convertToListVo(sheetPage); + return sheetAssembler.convertToListVo(sheetPage); } @GetMapping("independent") @@ -88,7 +93,7 @@ public SheetDetailVO createBy(@RequestBody @Valid SheetParam sheetParam, Boolean autoSave) { Sheet sheet = sheetService.createBy(sheetParam.convertTo(), sheetParam.getSheetMetas(), autoSave); - return sheetService.convertToDetailVo(sheet); + return sheetAssembler.convertToDetailVo(sheet); } @PutMapping("{sheetId:\\d+}") @@ -104,7 +109,7 @@ public SheetDetailVO updateBy( Sheet sheet = sheetService.updateBy(sheetToUpdate, sheetParam.getSheetMetas(), autoSave); - return sheetService.convertToDetailVo(sheet); + return sheetAssembler.convertToDetailVo(sheet); } @PutMapping("{sheetId:\\d+}/{status}") @@ -132,14 +137,14 @@ public BasePostDetailDTO updateDraftBy( // Update draft content Sheet sheet = sheetService.updateDraftContent(formattedContent, contentParam.getOriginalContent(), sheetId); - return sheetService.convertToDetail(sheet); + return sheetAssembler.convertToDetail(sheet); } @DeleteMapping("{sheetId:\\d+}") @ApiOperation("Deletes a sheet") public SheetDetailVO deleteBy(@PathVariable("sheetId") Integer sheetId) { Sheet sheet = sheetService.removeById(sheetId); - return sheetService.convertToDetailVo(sheet); + return sheetAssembler.convertToDetailVo(sheet); } @GetMapping("preview/{sheetId:\\d+}") @@ -150,7 +155,7 @@ public String preview(@PathVariable("sheetId") Integer sheetId) sheet.setSlug(URLEncoder.encode(sheet.getSlug(), StandardCharsets.UTF_8.name())); - BasePostMinimalDTO sheetMinimalDTO = sheetService.convertToMinimal(sheet); + BasePostMinimalDTO sheetMinimalDTO = sheetAssembler.convertToMinimal(sheet); String token = HaloUtils.simpleUUID(); diff --git a/src/main/java/run/halo/app/controller/content/ContentContentController.java b/src/main/java/run/halo/app/controller/content/ContentContentController.java index 985a43d778..6e919de011 100644 --- a/src/main/java/run/halo/app/controller/content/ContentContentController.java +++ b/src/main/java/run/halo/app/controller/content/ContentContentController.java @@ -43,6 +43,7 @@ import run.halo.app.service.PostService; import run.halo.app.service.SheetService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * @author ryanwang @@ -78,6 +79,8 @@ public class ContentContentController { private final ThemeService themeService; + private final PostRenderAssembler postRenderAssembler; + private final ContentAuthenticationManager providerManager; public ContentContentController(PostModel postModel, @@ -92,6 +95,7 @@ public ContentContentController(PostModel postModel, SheetService sheetService, CategoryService categoryService, ThemeService themeService, + PostRenderAssembler postRenderAssembler, ContentAuthenticationManager providerManager) { this.postModel = postModel; this.sheetModel = sheetModel; @@ -105,6 +109,7 @@ public ContentContentController(PostModel postModel, this.sheetService = sheetService; this.categoryService = categoryService; this.themeService = themeService; + this.postRenderAssembler = postRenderAssembler; this.providerManager = providerManager; } @@ -270,7 +275,7 @@ private String authenticatePost(String slug, String type, String password, authRequest.setPrincipal(EncryptTypeEnum.POST.getName()); try { providerManager.authenticate(authRequest); - BasePostMinimalDTO basePostMinimal = postService.convertToMinimal(post); + BasePostMinimalDTO basePostMinimal = postRenderAssembler.convertToMinimal(post); return "redirect:" + buildRedirectUrl(basePostMinimal.getFullPath()); } catch (AuthenticationException e) { request.setAttribute("errorMsg", e.getMessage()); diff --git a/src/main/java/run/halo/app/controller/content/ContentFeedController.java b/src/main/java/run/halo/app/controller/content/ContentFeedController.java index 633de978d2..7122ef14d6 100644 --- a/src/main/java/run/halo/app/controller/content/ContentFeedController.java +++ b/src/main/java/run/halo/app/controller/content/ContentFeedController.java @@ -30,7 +30,6 @@ import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; import run.halo.app.model.dto.CategoryDTO; import run.halo.app.model.entity.Category; -import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Post; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.vo.PostDetailVO; @@ -38,6 +37,7 @@ import run.halo.app.service.OptionService; import run.halo.app.service.PostCategoryService; import run.halo.app.service.PostService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * @author ryanwang @@ -58,6 +58,8 @@ public class ContentFeedController { private final PostService postService; + private final PostRenderAssembler postRenderAssembler; + private final CategoryService categoryService; private final PostCategoryService postCategoryService; @@ -67,11 +69,12 @@ public class ContentFeedController { private final FreeMarkerConfigurer freeMarker; public ContentFeedController(PostService postService, - CategoryService categoryService, + PostRenderAssembler postRenderAssembler, CategoryService categoryService, PostCategoryService postCategoryService, OptionService optionService, FreeMarkerConfigurer freeMarker) { this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.categoryService = categoryService; this.postCategoryService = postCategoryService; this.optionService = optionService; @@ -258,14 +261,7 @@ private List buildPosts(@NonNull Pageable pageable) { @NonNull private Page convertToDetailPageVo(Page postPage) { Assert.notNull(postPage, "The postPage must not be null."); - - // Populate post content - postPage.getContent().forEach(post -> { - Content postContent = postService.getContentById(post.getId()); - post.setContent(Content.PatchedContent.of(postContent)); - }); - - Page posts = postService.convertToDetailVo(postPage); + Page posts = postRenderAssembler.convertToDetailVo(postPage); posts.getContent().forEach(postDetailVO -> { postDetailVO.setContent( RegExUtils.replaceAll(postDetailVO.getContent(), XML_INVALID_CHAR, "")); diff --git a/src/main/java/run/halo/app/controller/content/ContentSearchController.java b/src/main/java/run/halo/app/controller/content/ContentSearchController.java index b24cb10664..028375f821 100644 --- a/src/main/java/run/halo/app/controller/content/ContentSearchController.java +++ b/src/main/java/run/halo/app/controller/content/ContentSearchController.java @@ -19,6 +19,7 @@ import run.halo.app.service.OptionService; import run.halo.app.service.PostService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Search controller. @@ -32,13 +33,17 @@ public class ContentSearchController { private final PostService postService; + private final PostRenderAssembler postRenderAssembler; + private final OptionService optionService; private final ThemeService themeService; - public ContentSearchController(PostService postService, OptionService optionService, + public ContentSearchController(PostService postService, + PostRenderAssembler postRenderAssembler, OptionService optionService, ThemeService themeService) { this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.optionService = optionService; this.themeService = themeService; } @@ -71,7 +76,7 @@ public String search(Model model, final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), sort); final Page postPage = postService.pageBy(keyword, pageable); - final Page posts = postService.convertToListVo(postPage); + final Page posts = postRenderAssembler.convertToListVo(postPage); model.addAttribute("is_search", true); model.addAttribute("keyword", HtmlUtils.htmlEscape(keyword)); diff --git a/src/main/java/run/halo/app/controller/content/api/CategoryController.java b/src/main/java/run/halo/app/controller/content/api/CategoryController.java index 8eeda9d3f0..74e8d9708d 100644 --- a/src/main/java/run/halo/app/controller/content/api/CategoryController.java +++ b/src/main/java/run/halo/app/controller/content/api/CategoryController.java @@ -29,7 +29,7 @@ import run.halo.app.model.vo.PostListVO; import run.halo.app.service.CategoryService; import run.halo.app.service.PostCategoryService; -import run.halo.app.service.PostService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Content category controller. @@ -45,7 +45,7 @@ public class CategoryController { private final PostCategoryService postCategoryService; - private final PostService postService; + private final PostRenderAssembler postRenderAssembler; private final CategoryAuthentication categoryAuthentication; @@ -53,12 +53,12 @@ public class CategoryController { public CategoryController(CategoryService categoryService, PostCategoryService postCategoryService, - PostService postService, + PostRenderAssembler postRenderAssembler, CategoryAuthentication categoryAuthentication, ContentAuthenticationManager contentAuthenticationManager) { this.categoryService = categoryService; this.postCategoryService = postCategoryService; - this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.categoryAuthentication = categoryAuthentication; this.contentAuthenticationManager = contentAuthenticationManager; } @@ -90,7 +90,7 @@ public Page listPostsBy(@PathVariable("slug") String slug, Page postPage = postCategoryService.pagePostBy(category.getId(), statusesToQuery, pageable); - return postService.convertToListVo(postPage); + return postRenderAssembler.convertToListVo(postPage); } private boolean allowIntimatePosts(Integer categoryId, String password) { diff --git a/src/main/java/run/halo/app/controller/content/api/PostController.java b/src/main/java/run/halo/app/controller/content/api/PostController.java index bafed62c04..76ec82b4b3 100644 --- a/src/main/java/run/halo/app/controller/content/api/PostController.java +++ b/src/main/java/run/halo/app/controller/content/api/PostController.java @@ -22,10 +22,11 @@ import org.springframework.web.util.HtmlUtils; import run.halo.app.cache.lock.CacheLock; import run.halo.app.cache.lock.CacheParam; +import run.halo.app.controller.content.auth.PostAuthentication; +import run.halo.app.exception.ForbiddenException; import run.halo.app.exception.NotFoundException; import run.halo.app.model.dto.BaseCommentDTO; import run.halo.app.model.dto.post.BasePostSimpleDTO; -import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Post; import run.halo.app.model.entity.PostComment; import run.halo.app.model.enums.CommentStatus; @@ -40,6 +41,7 @@ import run.halo.app.service.OptionService; import run.halo.app.service.PostCommentService; import run.halo.app.service.PostService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Content post controller. @@ -59,18 +61,25 @@ public class PostController { private final OptionService optionService; + private final PostRenderAssembler postRenderAssembler; + + private final PostAuthentication postAuthentication; + public PostController(PostService postService, PostCommentService postCommentService, - OptionService optionService) { + OptionService optionService, PostRenderAssembler postRenderAssembler, + PostAuthentication postAuthentication) { this.postService = postService; this.postCommentService = postCommentService; this.optionService = optionService; + this.postRenderAssembler = postRenderAssembler; + this.postAuthentication = postAuthentication; } //CS304 issue for https://github.com/halo-dev/halo/issues/1351 /** - * Enable users search published articles with keywords + * Enable users search published articles with keywords. * * @param pageable store the priority of the sort algorithm * @param keyword search articles with keyword @@ -88,7 +97,7 @@ public Page pageBy( postQuery.setCategoryId(categoryId); postQuery.setStatuses(Set.of(PostStatus.PUBLISHED)); Page postPage = postService.pageBy(postQuery, pageable); - return postService.convertToListVo(postPage, true); + return postRenderAssembler.convertToListVo(postPage); } @PostMapping(value = "search") @@ -96,7 +105,7 @@ public Page pageBy( public Page pageBy(@RequestParam(value = "keyword") String keyword, @PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) { Page postPage = postService.pageBy(keyword, pageable); - return postService.convertToSimple(postPage); + return postRenderAssembler.convertToSimple(postPage); } @GetMapping("{postId:\\d+}") @@ -107,8 +116,10 @@ public PostDetailVO getBy(@PathVariable("postId") Integer postId, @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) { Post post = postService.getById(postId); - post.setContent(Content.PatchedContent.of(postService.getContentById(postId))); - PostDetailVO postDetailVO = postService.convertToDetailVo(post); + + checkAuthenticate(postId); + + PostDetailVO postDetailVO = postRenderAssembler.convertToDetailVo(post); if (formatDisabled) { // Clear the format content @@ -133,8 +144,10 @@ public PostDetailVO getBy(@RequestParam("slug") String slug, @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) { Post post = postService.getBySlug(slug); - post.setContent(Content.PatchedContent.of(postService.getContentById(post.getId()))); - PostDetailVO postDetailVO = postService.convertToDetailVo(post); + + checkAuthenticate(post.getId()); + + PostDetailVO postDetailVO = postRenderAssembler.convertToDetailVo(post); if (formatDisabled) { // Clear the format content @@ -157,9 +170,8 @@ public PostDetailVO getPrevPostBy(@PathVariable("postId") Integer postId) { Post post = postService.getById(postId); Post prevPost = postService.getPrevPost(post).orElseThrow(() -> new NotFoundException("查询不到该文章的信息")); - prevPost.setContent( - Content.PatchedContent.of(postService.getContentById(prevPost.getId()))); - return postService.convertToDetailVo(prevPost); + checkAuthenticate(prevPost.getId()); + return postRenderAssembler.convertToDetailVo(prevPost); } @GetMapping("{postId:\\d+}/next") @@ -168,15 +180,15 @@ public PostDetailVO getNextPostBy(@PathVariable("postId") Integer postId) { Post post = postService.getById(postId); Post nextPost = postService.getNextPost(post).orElseThrow(() -> new NotFoundException("查询不到该文章的信息")); - nextPost.setContent( - Content.PatchedContent.of(postService.getContentById(nextPost.getId()))); - return postService.convertToDetailVo(nextPost); + checkAuthenticate(nextPost.getId()); + return postRenderAssembler.convertToDetailVo(nextPost); } @GetMapping("{postId:\\d+}/comments/top_view") public Page listTopComments(@PathVariable("postId") Integer postId, @RequestParam(name = "page", required = false, defaultValue = "0") int page, @SortDefault(sort = "createTime", direction = DESC) Sort sort) { + checkAuthenticate(postId); return postCommentService.pageTopCommentsBy(postId, CommentStatus.PUBLISHED, PageRequest.of(page, optionService.getCommentPageSize(), sort)); } @@ -185,6 +197,7 @@ public Page listTopComments(@PathVariable("postId") In public List listChildrenBy(@PathVariable("postId") Integer postId, @PathVariable("commentParentId") Long commentParentId, @SortDefault(sort = "createTime", direction = DESC) Sort sort) { + checkAuthenticate(postId); // Find all children comments List postComments = postCommentService .listChildrenBy(postId, commentParentId, CommentStatus.PUBLISHED, sort); @@ -198,6 +211,7 @@ public List listChildrenBy(@PathVariable("postId") Integer postI public Page listCommentsTree(@PathVariable("postId") Integer postId, @RequestParam(name = "page", required = false, defaultValue = "0") int page, @SortDefault(sort = "createTime", direction = DESC) Sort sort) { + checkAuthenticate(postId); return postCommentService .pageVosBy(postId, PageRequest.of(page, optionService.getCommentPageSize(), sort)); } @@ -207,6 +221,7 @@ public Page listCommentsTree(@PathVariable("postId") Integer post public Page listComments(@PathVariable("postId") Integer postId, @RequestParam(name = "page", required = false, defaultValue = "0") int page, @SortDefault(sort = "createTime", direction = DESC) Sort sort) { + checkAuthenticate(postId); return postCommentService.pageWithParentVoBy(postId, PageRequest.of(page, optionService.getCommentPageSize(), sort)); } @@ -215,6 +230,7 @@ public Page listComments(@PathVariable("postId") Intege @ApiOperation("Comments a post") @CacheLock(autoDelete = false, traceRequest = true) public BaseCommentDTO comment(@RequestBody PostCommentParam postCommentParam) { + checkAuthenticate(postCommentParam.getPostId()); postCommentService.validateCommentBlackListStatus(); // Escape content @@ -227,6 +243,13 @@ public BaseCommentDTO comment(@RequestBody PostCommentParam postCommentParam) { @ApiOperation("Likes a post") @CacheLock(autoDelete = false, traceRequest = true) public void like(@PathVariable("postId") @CacheParam Integer postId) { + checkAuthenticate(postId); postService.increaseLike(postId); } + + private void checkAuthenticate(Integer postId) { + if (!postAuthentication.isAuthenticated(postId)) { + throw new ForbiddenException("您没有该分类的访问权限"); + } + } } diff --git a/src/main/java/run/halo/app/controller/content/api/SheetController.java b/src/main/java/run/halo/app/controller/content/api/SheetController.java index b5f01aef70..88e6b36409 100644 --- a/src/main/java/run/halo/app/controller/content/api/SheetController.java +++ b/src/main/java/run/halo/app/controller/content/api/SheetController.java @@ -21,7 +21,6 @@ import org.springframework.web.util.HtmlUtils; import run.halo.app.cache.lock.CacheLock; import run.halo.app.model.dto.BaseCommentDTO; -import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Sheet; import run.halo.app.model.entity.SheetComment; import run.halo.app.model.enums.CommentStatus; @@ -35,6 +34,7 @@ import run.halo.app.service.OptionService; import run.halo.app.service.SheetCommentService; import run.halo.app.service.SheetService; +import run.halo.app.service.assembler.SheetRenderAssembler; /** * Content sheet controller. @@ -49,13 +49,18 @@ public class SheetController { private final SheetService sheetService; + private final SheetRenderAssembler sheetRenderAssembler; + private final SheetCommentService sheetCommentService; private final OptionService optionService; - public SheetController(SheetService sheetService, SheetCommentService sheetCommentService, + public SheetController(SheetService sheetService, + SheetRenderAssembler sheetRenderAssembler, + SheetCommentService sheetCommentService, OptionService optionService) { this.sheetService = sheetService; + this.sheetRenderAssembler = sheetRenderAssembler; this.sheetCommentService = sheetCommentService; this.optionService = optionService; } @@ -65,7 +70,7 @@ public SheetController(SheetService sheetService, SheetCommentService sheetComme public Page pageBy( @PageableDefault(sort = "createTime", direction = DESC) Pageable pageable) { Page sheetPage = sheetService.pageBy(PostStatus.PUBLISHED, pageable); - return sheetService.convertToListVo(sheetPage); + return sheetRenderAssembler.convertToListVo(sheetPage); } @GetMapping("{sheetId:\\d+}") @@ -76,8 +81,8 @@ public SheetDetailVO getBy(@PathVariable("sheetId") Integer sheetId, @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) { Sheet sheet = sheetService.getById(sheetId); - sheet.setContent(Content.PatchedContent.of(sheetService.getContentById(sheetId))); - SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet); + + SheetDetailVO sheetDetailVO = sheetRenderAssembler.convertToDetailVo(sheet); if (formatDisabled) { // Clear the format content @@ -102,8 +107,7 @@ public SheetDetailVO getBy(@RequestParam("slug") String slug, @RequestParam(value = "sourceDisabled", required = false, defaultValue = "false") Boolean sourceDisabled) { Sheet sheet = sheetService.getBySlug(slug); - sheet.setContent(Content.PatchedContent.of(sheetService.getContentById(sheet.getId()))); - SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet); + SheetDetailVO sheetDetailVO = sheetRenderAssembler.convertToDetailVo(sheet); if (formatDisabled) { // Clear the format content diff --git a/src/main/java/run/halo/app/controller/content/api/TagController.java b/src/main/java/run/halo/app/controller/content/api/TagController.java index 56ed94256c..ee68795df5 100644 --- a/src/main/java/run/halo/app/controller/content/api/TagController.java +++ b/src/main/java/run/halo/app/controller/content/api/TagController.java @@ -20,9 +20,9 @@ import run.halo.app.model.entity.Tag; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.vo.PostListVO; -import run.halo.app.service.PostService; import run.halo.app.service.PostTagService; import run.halo.app.service.TagService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Content tag controller. @@ -39,14 +39,14 @@ public class TagController { private final PostTagService postTagService; - private final PostService postService; + private final PostRenderAssembler postRenderAssembler; public TagController(TagService tagService, PostTagService postTagService, - PostService postService) { + PostRenderAssembler postRenderAssembler) { this.tagService = tagService; this.postTagService = postTagService; - this.postService = postService; + this.postRenderAssembler = postRenderAssembler; } @GetMapping @@ -72,6 +72,6 @@ public Page listPostsBy(@PathVariable("slug") String slug, // Get posts, convert and return Page postPage = postTagService.pagePostsBy(tag.getId(), PostStatus.PUBLISHED, pageable); - return postService.convertToListVo(postPage); + return postRenderAssembler.convertToListVo(postPage); } } diff --git a/src/main/java/run/halo/app/controller/content/auth/CategoryAuthentication.java b/src/main/java/run/halo/app/controller/content/auth/CategoryAuthentication.java index ca4a1c3452..8c4f823f90 100644 --- a/src/main/java/run/halo/app/controller/content/auth/CategoryAuthentication.java +++ b/src/main/java/run/halo/app/controller/content/auth/CategoryAuthentication.java @@ -37,7 +37,7 @@ public Object getPrincipal() { @Override public boolean isAuthenticated(Integer categoryId) { Category category = categoryService.getById(categoryId); - if (category.getPassword() == null) { + if (StringUtils.isBlank(category.getPassword())) { // All parent category is not encrypted if (categoryService.lookupFirstEncryptedBy(category.getId()).isEmpty()) { return true; diff --git a/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java b/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java index a6be3f7ab1..85547fba08 100644 --- a/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java +++ b/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java @@ -6,6 +6,8 @@ import run.halo.app.cache.AbstractStringCacheStore; import run.halo.app.model.entity.Post; import run.halo.app.model.enums.EncryptTypeEnum; +import run.halo.app.service.CategoryService; +import run.halo.app.service.PostCategoryService; import run.halo.app.service.PostService; /** @@ -18,11 +20,17 @@ public class PostAuthentication implements ContentAuthentication { private final PostService postService; + private final CategoryService categoryService; + private final PostCategoryService postCategoryService; private final AbstractStringCacheStore cacheStore; public PostAuthentication(PostService postService, + CategoryService categoryService, + PostCategoryService postCategoryService, AbstractStringCacheStore cacheStore) { this.postService = postService; + this.categoryService = categoryService; + this.postCategoryService = postCategoryService; this.cacheStore = cacheStore; } @@ -34,8 +42,12 @@ public Object getPrincipal() { @Override public boolean isAuthenticated(Integer postId) { Post post = postService.getById(postId); - if (post.getPassword() == null) { - return true; + if (StringUtils.isBlank(post.getPassword())) { + boolean categoryEncrypted = postCategoryService.listByPostId(postId).stream() + .anyMatch(postCategory -> categoryService.isPrivate(postCategory.getCategoryId())); + if (!categoryEncrypted) { + return true; + } } String sessionId = getSessionId(); diff --git a/src/main/java/run/halo/app/controller/content/model/CategoryModel.java b/src/main/java/run/halo/app/controller/content/model/CategoryModel.java index d870e5d1df..ce996fa7a9 100644 --- a/src/main/java/run/halo/app/controller/content/model/CategoryModel.java +++ b/src/main/java/run/halo/app/controller/content/model/CategoryModel.java @@ -23,13 +23,14 @@ import run.halo.app.service.CategoryService; import run.halo.app.service.OptionService; import run.halo.app.service.PostCategoryService; -import run.halo.app.service.PostService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Category Model. * * @author ryanwang + * @author guqing * @date 2020-01-11 */ @Component @@ -41,7 +42,7 @@ public class CategoryModel { private final PostCategoryService postCategoryService; - private final PostService postService; + private final PostRenderAssembler postRenderAssembler; private final OptionService optionService; @@ -50,13 +51,12 @@ public class CategoryModel { public CategoryModel(CategoryService categoryService, ThemeService themeService, PostCategoryService postCategoryService, - PostService postService, - OptionService optionService, + PostRenderAssembler postRenderAssembler, OptionService optionService, CategoryAuthentication categoryAuthentication) { this.categoryService = categoryService; this.themeService = themeService; this.postCategoryService = postCategoryService; - this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.optionService = optionService; this.categoryAuthentication = categoryAuthentication; } @@ -108,7 +108,7 @@ public String listPost(Model model, String slug, Integer page) { Sort.by(DESC, "topPriority", "createTime")); Page postPage = postCategoryService.pagePostBy(category.getId(), statuses, pageable); - Page posts = postService.convertToListVo(postPage); + Page posts = postRenderAssembler.convertToListVo(postPage); // Generate meta description. if (StringUtils.isNotEmpty(category.getDescription())) { diff --git a/src/main/java/run/halo/app/controller/content/model/PostModel.java b/src/main/java/run/halo/app/controller/content/model/PostModel.java index 0858501253..0da00281dc 100644 --- a/src/main/java/run/halo/app/controller/content/model/PostModel.java +++ b/src/main/java/run/halo/app/controller/content/model/PostModel.java @@ -17,8 +17,6 @@ import run.halo.app.exception.ForbiddenException; import run.halo.app.exception.NotFoundException; import run.halo.app.model.entity.Category; -import run.halo.app.model.entity.Content; -import run.halo.app.model.entity.Content.PatchedContent; import run.halo.app.model.entity.Post; import run.halo.app.model.entity.PostMeta; import run.halo.app.model.entity.Tag; @@ -34,6 +32,7 @@ import run.halo.app.service.PostTagService; import run.halo.app.service.TagService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Post Model @@ -45,6 +44,8 @@ @Component public class PostModel { + private final PostRenderAssembler postRenderAssembler; + private final PostService postService; private final ThemeService themeService; @@ -65,7 +66,8 @@ public class PostModel { private final PostAuthentication postAuthentication; - public PostModel(PostService postService, + public PostModel(PostRenderAssembler postRenderAssembler, + PostService postService, ThemeService themeService, PostCategoryService postCategoryService, CategoryService categoryService, @@ -75,6 +77,7 @@ public PostModel(PostService postService, OptionService optionService, AbstractStringCacheStore cacheStore, PostAuthentication postAuthentication) { + this.postRenderAssembler = postRenderAssembler; this.postService = postService; this.themeService = themeService; this.postCategoryService = postCategoryService; @@ -117,21 +120,16 @@ public String content(Post post, String token, Model model) { return "common/template/" + POST_PASSWORD_TEMPLATE; } - if (StringUtils.isNotBlank(token)) { - post = postService.getWithLatestContentById(post.getId()); - } else { - post = postService.getById(post.getId()); - // Set post content - Content postContent = postService.getContentById(post.getId()); - post.setContent(PatchedContent.of(postContent)); - } + post = postService.getById(post.getId()); postService.publishVisitEvent(post.getId()); - postService.getPrevPost(post).ifPresent( - prevPost -> model.addAttribute("prevPost", postService.convertToDetailVo(prevPost))); - postService.getNextPost(post).ifPresent( - nextPost -> model.addAttribute("nextPost", postService.convertToDetailVo(nextPost))); + postService.getPrevPost(post) + .ifPresent(prevPost -> model.addAttribute("prevPost", + postRenderAssembler.convertToDetailVo(prevPost))); + postService.getNextPost(post) + .ifPresent(nextPost -> model.addAttribute("nextPost", + postRenderAssembler.convertToDetailVo(nextPost))); List categories = postCategoryService.listCategoriesBy(post.getId()); List tags = postTagService.listTagsBy(post.getId()); @@ -154,7 +152,13 @@ public String content(Post post, String token, Model model) { } model.addAttribute("is_post", true); - model.addAttribute("post", postService.convertToDetailVo(post)); + + if (StringUtils.isNotBlank(token)) { + model.addAttribute("post", postRenderAssembler.convertToPreviewDetailVo(post)); + } else { + model.addAttribute("post", postRenderAssembler.convertToDetailVo(post)); + } + model.addAttribute("categories", categoryService.convertTo(categories)); model.addAttribute("tags", tagService.convertTo(tags)); model.addAttribute("metas", postMetaService.convertToMap(metas)); @@ -173,7 +177,7 @@ public String list(Integer page, Model model) { .of(page >= 1 ? page - 1 : page, pageSize, postService.getPostDefaultSort()); Page postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); - Page posts = postService.convertToListVo(postPage); + Page posts = postRenderAssembler.convertToListVo(postPage); model.addAttribute("is_index", true); model.addAttribute("posts", posts); @@ -189,9 +193,9 @@ public String archives(Integer page, Model model) { Page postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); - Page posts = postService.convertToListVo(postPage); + Page posts = postRenderAssembler.convertToListVo(postPage); - List archives = postService.convertToYearArchives(postPage.getContent()); + List archives = postRenderAssembler.convertToYearArchives(postPage.getContent()); model.addAttribute("is_archives", true); model.addAttribute("posts", posts); diff --git a/src/main/java/run/halo/app/controller/content/model/SheetModel.java b/src/main/java/run/halo/app/controller/content/model/SheetModel.java index 2d8777c43b..93b945877b 100644 --- a/src/main/java/run/halo/app/controller/content/model/SheetModel.java +++ b/src/main/java/run/halo/app/controller/content/model/SheetModel.java @@ -6,8 +6,6 @@ import org.springframework.ui.Model; import run.halo.app.cache.AbstractStringCacheStore; import run.halo.app.exception.ForbiddenException; -import run.halo.app.model.entity.Content; -import run.halo.app.model.entity.Content.PatchedContent; import run.halo.app.model.entity.Sheet; import run.halo.app.model.entity.SheetMeta; import run.halo.app.model.enums.PostStatus; @@ -17,6 +15,7 @@ import run.halo.app.service.SheetMetaService; import run.halo.app.service.SheetService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.SheetRenderAssembler; /** * Sheet model. @@ -29,6 +28,8 @@ public class SheetModel { private final SheetService sheetService; + private final SheetRenderAssembler sheetRenderAssembler; + private final SheetMetaService sheetMetaService; private final AbstractStringCacheStore cacheStore; @@ -38,11 +39,13 @@ public class SheetModel { private final OptionService optionService; public SheetModel(SheetService sheetService, + SheetRenderAssembler sheetRenderAssembler, SheetMetaService sheetMetaService, AbstractStringCacheStore cacheStore, ThemeService themeService, OptionService optionService) { this.sheetService = sheetService; + this.sheetRenderAssembler = sheetRenderAssembler; this.sheetMetaService = sheetMetaService; this.cacheStore = cacheStore; this.themeService = themeService; @@ -58,12 +61,10 @@ public SheetModel(SheetService sheetService, * @return template name */ public String content(Sheet sheet, String token, Model model) { - + SheetDetailVO sheetDetailVo; if (StringUtils.isEmpty(token)) { sheet = sheetService.getBy(PostStatus.PUBLISHED, sheet.getSlug()); - //Set sheet content - Content content = sheetService.getContentById(sheet.getId()); - sheet.setContent(PatchedContent.of(content)); + sheetDetailVo = sheetRenderAssembler.convertToDetailVo(sheet); } else { // verify token String cachedToken = cacheStore.getAny(token, String.class) @@ -71,15 +72,11 @@ public String content(Sheet sheet, String token, Model model) { if (!cachedToken.equals(token)) { throw new ForbiddenException("您没有该页面的访问权限"); } - // render markdown to html when preview sheet - PatchedContent sheetContent = sheetService.getLatestContentById(sheet.getId()); - sheet.setContent(sheetContent); + sheetDetailVo = sheetRenderAssembler.convertToPreviewDetailVo(sheet); } sheetService.publishVisitEvent(sheet.getId()); - SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet); - List metas = sheetMetaService.listBy(sheet.getId()); // Generate meta keywords. @@ -98,8 +95,8 @@ public String content(Sheet sheet, String token, Model model) { } // sheet and post all can use - model.addAttribute("sheet", sheetDetailVO); - model.addAttribute("post", sheetDetailVO); + model.addAttribute("sheet", sheetDetailVo); + model.addAttribute("post", sheetDetailVo); model.addAttribute("is_sheet", true); model.addAttribute("metas", sheetMetaService.convertToMap(metas)); diff --git a/src/main/java/run/halo/app/controller/content/model/TagModel.java b/src/main/java/run/halo/app/controller/content/model/TagModel.java index 5bffd42bff..b461824e5d 100644 --- a/src/main/java/run/halo/app/controller/content/model/TagModel.java +++ b/src/main/java/run/halo/app/controller/content/model/TagModel.java @@ -14,10 +14,10 @@ import run.halo.app.model.enums.PostStatus; import run.halo.app.model.vo.PostListVO; import run.halo.app.service.OptionService; -import run.halo.app.service.PostService; import run.halo.app.service.PostTagService; import run.halo.app.service.TagService; import run.halo.app.service.ThemeService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Tag Model. @@ -30,7 +30,7 @@ public class TagModel { private final TagService tagService; - private final PostService postService; + private final PostRenderAssembler postRenderAssembler; private final PostTagService postTagService; @@ -38,10 +38,13 @@ public class TagModel { private final ThemeService themeService; - public TagModel(TagService tagService, PostService postService, PostTagService postTagService, - OptionService optionService, ThemeService themeService) { + public TagModel(TagService tagService, + PostRenderAssembler postRenderAssembler, + PostTagService postTagService, + OptionService optionService, + ThemeService themeService) { this.tagService = tagService; - this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.postTagService = postTagService; this.optionService = optionService; this.themeService = themeService; @@ -63,7 +66,7 @@ public String listPost(Model model, String slug, Integer page) { .of(page - 1, optionService.getArchivesPageSize(), Sort.by(DESC, "createTime")); Page postPage = postTagService.pagePostsBy(tag.getId(), PostStatus.PUBLISHED, pageable); - Page posts = postService.convertToListVo(postPage); + Page posts = postRenderAssembler.convertToListVo(postPage); model.addAttribute("is_tag", true); model.addAttribute("posts", posts); diff --git a/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java b/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java index b27d70e50a..3283146dfb 100644 --- a/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java +++ b/src/main/java/run/halo/app/core/freemarker/tag/PostTagDirective.java @@ -18,6 +18,7 @@ import run.halo.app.service.PostCategoryService; import run.halo.app.service.PostService; import run.halo.app.service.PostTagService; +import run.halo.app.service.assembler.PostRenderAssembler; /** * Freemarker custom tag of post. @@ -30,15 +31,19 @@ public class PostTagDirective implements TemplateDirectiveModel { private final PostService postService; + private final PostRenderAssembler postRenderAssembler; + private final PostTagService postTagService; private final PostCategoryService postCategoryService; public PostTagDirective(Configuration configuration, PostService postService, + PostRenderAssembler postRenderAssembler, PostTagService postTagService, PostCategoryService postCategoryService) { this.postService = postService; + this.postRenderAssembler = postRenderAssembler; this.postTagService = postTagService; this.postCategoryService = postCategoryService; configuration.setSharedVariable("postTag", this); @@ -55,7 +60,7 @@ public void execute(Environment env, Map params, TemplateModel[] loopVars, case "latest": int top = Integer.parseInt(params.get("top").toString()); env.setVariable("posts", builder.build() - .wrap(postService.convertToListVo(postService.listLatest(top)))); + .wrap(postRenderAssembler.convertToListVo(postService.listLatest(top)))); break; case "count": env.setVariable("count", @@ -77,9 +82,11 @@ public void execute(Environment env, Map params, TemplateModel[] loopVars, break; case "listByCategoryId": Integer categoryId = Integer.parseInt(params.get("categoryId").toString()); - env.setVariable("posts", builder.build().wrap(postService.convertToListVo( - postCategoryService.listPostBy(categoryId, - Sets.immutableEnumSet(PostStatus.PUBLISHED, PostStatus.INTIMATE))))); + env.setVariable("posts", builder.build() + .wrap(postRenderAssembler.convertToListVo( + postCategoryService.listPostBy(categoryId, + Sets.immutableEnumSet(PostStatus.PUBLISHED, + PostStatus.INTIMATE))))); break; case "listByCategorySlug": String categorySlug = params.get("categorySlug").toString(); @@ -87,17 +94,22 @@ public void execute(Environment env, Map params, TemplateModel[] loopVars, postCategoryService.listPostBy(categorySlug, Sets.immutableEnumSet(PostStatus.PUBLISHED, PostStatus.INTIMATE)); env.setVariable("posts", - builder.build().wrap(postService.convertToListVo(posts))); + builder.build().wrap(postRenderAssembler.convertToListVo(posts))); break; case "listByTagId": Integer tagId = Integer.parseInt(params.get("tagId").toString()); - env.setVariable("posts", builder.build().wrap(postService + env.setVariable("posts", builder.build().wrap(postRenderAssembler .convertToListVo(postTagService.listPostsBy(tagId, PostStatus.PUBLISHED)))); break; case "listByTagSlug": String tagSlug = params.get("tagSlug").toString(); - env.setVariable("posts", builder.build().wrap(postService.convertToListVo( - postTagService.listPostsBy(tagSlug, PostStatus.PUBLISHED)))); + env.setVariable("posts", builder.build() + .wrap( + postRenderAssembler.convertToListVo( + postTagService.listPostsBy(tagSlug, PostStatus.PUBLISHED) + ) + ) + ); break; default: break; diff --git a/src/main/java/run/halo/app/listener/comment/CommentEventListener.java b/src/main/java/run/halo/app/listener/comment/CommentEventListener.java index d9fae8b03c..bba9913b82 100644 --- a/src/main/java/run/halo/app/listener/comment/CommentEventListener.java +++ b/src/main/java/run/halo/app/listener/comment/CommentEventListener.java @@ -27,6 +27,8 @@ import run.halo.app.service.SheetService; import run.halo.app.service.ThemeService; import run.halo.app.service.UserService; +import run.halo.app.service.assembler.PostAssembler; +import run.halo.app.service.assembler.SheetAssembler; import run.halo.app.utils.ValidationUtils; /** @@ -52,8 +54,12 @@ public class CommentEventListener { private final PostService postService; + private final PostAssembler postAssembler; + private final SheetService sheetService; + private final SheetAssembler sheetAssembler; + private final JournalService journalService; private final UserService userService; @@ -63,7 +69,9 @@ public class CommentEventListener { public CommentEventListener(MailService mailService, OptionService optionService, PostCommentService postCommentService, SheetCommentService sheetCommentService, JournalCommentService journalCommentService, PostService postService, - SheetService sheetService, JournalService journalService, UserService userService, + PostAssembler postAssembler, SheetService sheetService, + SheetAssembler sheetAssembler, JournalService journalService, + UserService userService, ThemeService themeService) { this.mailService = mailService; this.optionService = optionService; @@ -71,7 +79,9 @@ public CommentEventListener(MailService mailService, OptionService optionService this.sheetCommentService = sheetCommentService; this.journalCommentService = journalCommentService; this.postService = postService; + this.postAssembler = postAssembler; this.sheetService = sheetService; + this.sheetAssembler = sheetAssembler; this.journalService = journalService; this.userService = userService; this.themeService = themeService; @@ -109,7 +119,7 @@ public void handleCommentNewEvent(CommentNewEvent newEvent) { log.debug("Got post comment: [{}]", postComment); BasePostMinimalDTO post = - postService.convertToMinimal(postService.getById(postComment.getPostId())); + postAssembler.convertToMinimal(postService.getById(postComment.getPostId())); data.put("pageFullPath", enabledAbsolutePath ? post.getFullPath() : optionService.getBlogBaseUrl() + post.getFullPath()); @@ -127,7 +137,7 @@ public void handleCommentNewEvent(CommentNewEvent newEvent) { log.debug("Got sheet comment: [{}]", sheetComment); BasePostMinimalDTO sheet = - sheetService.convertToMinimal(sheetService.getById(sheetComment.getPostId())); + sheetAssembler.convertToMinimal(sheetService.getById(sheetComment.getPostId())); data.put("pageFullPath", enabledAbsolutePath ? sheet.getFullPath() : optionService.getBlogBaseUrl() + sheet.getFullPath()); @@ -211,7 +221,7 @@ public void handleCommentReplyEvent(CommentReplyEvent replyEvent) { baseAuthorEmail = baseComment.getEmail(); BasePostMinimalDTO post = - postService.convertToMinimal(postService.getById(postComment.getPostId())); + postAssembler.convertToMinimal(postService.getById(postComment.getPostId())); data.put("pageFullPath", enabledAbsolutePath ? post.getFullPath() : optionService.getBlogBaseUrl() + post.getFullPath()); @@ -244,7 +254,7 @@ public void handleCommentReplyEvent(CommentReplyEvent replyEvent) { baseAuthorEmail = baseComment.getEmail(); BasePostMinimalDTO sheet = - sheetService.convertToMinimal(sheetService.getById(sheetComment.getPostId())); + sheetAssembler.convertToMinimal(sheetService.getById(sheetComment.getPostId())); data.put("pageFullPath", enabledAbsolutePath ? sheet.getFullPath() : optionService.getBlogBaseUrl() + sheet.getFullPath()); diff --git a/src/main/java/run/halo/app/service/PostService.java b/src/main/java/run/halo/app/service/PostService.java index bb0a9d47c2..a2ad6bc253 100755 --- a/src/main/java/run/halo/app/service/PostService.java +++ b/src/main/java/run/halo/app/service/PostService.java @@ -15,7 +15,6 @@ import run.halo.app.model.vo.ArchiveMonthVO; import run.halo.app.model.vo.ArchiveYearVO; import run.halo.app.model.vo.PostDetailVO; -import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostMarkdownVO; import run.halo.app.service.base.BasePostService; @@ -187,22 +186,6 @@ Post getBy(@NonNull Integer year, @NonNull Integer month, @NonNull Integer day, @NonNull List listMonthArchives(); - /** - * Convert to year archives - * - * @param posts posts must not be null - * @return list of ArchiveYearVO - */ - List convertToYearArchives(@NonNull List posts); - - /** - * Convert to month archives - * - * @param posts posts must not be null - * @return list of ArchiveMonthVO - */ - List convertToMonthArchives(@NonNull List posts); - /** * Import post from markdown document. * @@ -231,61 +214,6 @@ Post getBy(@NonNull Integer year, @NonNull Integer month, @NonNull Integer day, @NonNull String exportMarkdown(@NonNull Post post); - /** - * Converts to detail vo. - * - * @param post post must not be null - * @return post detail vo - */ - @NonNull - PostDetailVO convertToDetailVo(@NonNull Post post); - - /** - * Converts to a page of detail vo. - * - * @param postPage post page must not be null - * @return a page of post detail vo - */ - Page convertToDetailVo(@NonNull Page postPage); - - /** - * Converts to detail vo. - * - * @param post post must not be null - * @param queryEncryptCategory whether to query encryption category - * @return post detail vo - */ - @NonNull - PostDetailVO convertToDetailVo(@NonNull Post post, @NonNull boolean queryEncryptCategory); - - /** - * Converts to a page of post list vo. - * - * @param postPage post page must not be null - * @return a page of post list vo - */ - @NonNull - Page convertToListVo(@NonNull Page postPage); - - /** - * Converts to a page of post list vo. - * - * @param postPage post page must not be null - * @param queryEncryptCategory whether to query encryption category - * @return a page of post list vo - */ - @NonNull - Page convertToListVo(@NonNull Page postPage, boolean queryEncryptCategory); - - /** - * Converts to a list of post list vo. - * - * @param posts post must not be null - * @return a list of post list vo - */ - @NonNull - List convertToListVo(@NonNull List posts); - /** * Publish a post visit event. * diff --git a/src/main/java/run/halo/app/service/SheetService.java b/src/main/java/run/halo/app/service/SheetService.java index 267b4aca65..210793e329 100644 --- a/src/main/java/run/halo/app/service/SheetService.java +++ b/src/main/java/run/halo/app/service/SheetService.java @@ -2,14 +2,11 @@ import java.util.List; import java.util.Set; -import org.springframework.data.domain.Page; import org.springframework.lang.NonNull; import run.halo.app.model.dto.IndependentSheetDTO; import run.halo.app.model.entity.Sheet; import run.halo.app.model.entity.SheetMeta; import run.halo.app.model.enums.PostStatus; -import run.halo.app.model.vo.SheetDetailVO; -import run.halo.app.model.vo.SheetListVO; import run.halo.app.service.base.BasePostService; /** @@ -17,6 +14,7 @@ * * @author johnniang * @author ryanwang + * @author guqing * @date 2019-04-24 */ public interface SheetService extends BasePostService { @@ -106,29 +104,10 @@ public interface SheetService extends BasePostService { @NonNull List listIndependentSheets(); - /** - * Converts to list dto page. - * - * @param sheetPage sheet page must not be nulls - * @return a page of sheet list dto - */ - @NonNull - Page convertToListVo(@NonNull Page sheetPage); - - /** - * Converts to detail vo. - * - * @param sheet sheet must not be null - * @return sheet detail vo - */ - @NonNull - SheetDetailVO convertToDetailVo(@NonNull Sheet sheet); - /** * Publish a sheet visit event. * * @param sheetId sheetId must not be null */ void publishVisitEvent(@NonNull Integer sheetId); - } diff --git a/src/main/java/run/halo/app/service/assembler/BasePostAssembler.java b/src/main/java/run/halo/app/service/assembler/BasePostAssembler.java new file mode 100644 index 0000000000..71c7303e92 --- /dev/null +++ b/src/main/java/run/halo/app/service/assembler/BasePostAssembler.java @@ -0,0 +1,188 @@ +package run.halo.app.service.assembler; + +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import run.halo.app.model.dto.post.BasePostDetailDTO; +import run.halo.app.model.dto.post.BasePostMinimalDTO; +import run.halo.app.model.dto.post.BasePostSimpleDTO; +import run.halo.app.model.entity.BasePost; +import run.halo.app.model.entity.Content; +import run.halo.app.model.entity.Content.PatchedContent; +import run.halo.app.model.properties.PostProperties; +import run.halo.app.service.ContentService; +import run.halo.app.service.OptionService; +import run.halo.app.utils.HaloUtils; + +/** + * @author guqing + * @date 2022-03-01 + */ +public class BasePostAssembler { + private static final Pattern summaryPattern = Pattern.compile("\t|\r|\n"); + + private final ContentService contentService; + + private final OptionService optionService; + + public BasePostAssembler(ContentService contentService, + OptionService optionService) { + this.contentService = contentService; + this.optionService = optionService; + } + + /** + * Convert POST to minimal dto. + * + * @param post post must not be null. + * @return minimal dto. + */ + public BasePostMinimalDTO convertToMinimal(POST post) { + Assert.notNull(post, "Post must not be null"); + + return new BasePostMinimalDTO().convertFrom(post); + } + + /** + * Convert list of POST to minimal dto of list. + * + * @param posts posts must not be null. + * @return a list of minimal dto. + */ + @NonNull + public List convertToMinimal(List posts) { + if (CollectionUtils.isEmpty(posts)) { + return Collections.emptyList(); + } + + return posts.stream() + .map(this::convertToMinimal) + .collect(Collectors.toList()); + } + + /** + * Convert page of POST to minimal dto of page. + * + * @param postPage postPage must not be null. + * @return a page of minimal dto. + */ + @NonNull + public Page convertToMinimal(Page postPage) { + Assert.notNull(postPage, "Post page must not be null"); + + return postPage.map(this::convertToMinimal); + } + + /** + * Convert POST to simple dto. + * + * @param post post must not be null. + * @return simple dto. + */ + @NonNull + public BasePostSimpleDTO convertToSimple(POST post) { + Assert.notNull(post, "Post must not be null"); + + BasePostSimpleDTO basePostSimpleDTO = new BasePostSimpleDTO().convertFrom(post); + + // Set summary + generateAndSetSummaryIfAbsent(post, basePostSimpleDTO); + + // Post currently drafting in process + Boolean isInProcess = contentService.draftingInProgress(post.getId()); + basePostSimpleDTO.setInProgress(isInProcess); + + return basePostSimpleDTO; + } + + /** + * Convert list of POST to list of simple dto. + * + * @param posts posts must not be null. + * @return a list of simple dto. + */ + @NonNull + public List convertToSimple(List posts) { + if (CollectionUtils.isEmpty(posts)) { + return Collections.emptyList(); + } + + return posts.stream() + .map(this::convertToSimple) + .collect(Collectors.toList()); + } + + /** + * Convert page of POST to page of simple dto. + * + * @param postPage postPage must not be null. + * @return a page of simple dto. + */ + @NonNull + public Page convertToSimple(Page postPage) { + Assert.notNull(postPage, "Post page must not be null"); + + return postPage.map(this::convertToSimple); + } + + /** + * Convert POST to detail dto. + * + * @param post post must not be null. + * @return detail dto. + */ + @NonNull + public BasePostDetailDTO convertToDetail(POST post) { + Assert.notNull(post, "Post must not be null"); + + BasePostDetailDTO postDetail = new BasePostDetailDTO().convertFrom(post); + + // Post currently drafting in process + Boolean isInProcess = contentService.draftingInProgress(post.getId()); + postDetail.setInProgress(isInProcess); + + return postDetail; + } + + @NonNull + protected String generateSummary(@Nullable String htmlContent) { + if (StringUtils.isBlank(htmlContent)) { + return StringUtils.EMPTY; + } + + String text = HaloUtils.cleanHtmlTag(htmlContent); + + Matcher matcher = summaryPattern.matcher(text); + text = matcher.replaceAll(""); + + // Get summary length + Integer summaryLength = + optionService.getByPropertyOrDefault(PostProperties.SUMMARY_LENGTH, Integer.class, 150); + + return StringUtils.substring(text, 0, summaryLength); + } + + protected void generateAndSetSummaryIfAbsent(POST post, + T postVo) { + Assert.notNull(post, "The post must not be null."); + if (StringUtils.isNotBlank(postVo.getSummary())) { + return; + } + + PatchedContent patchedContent = post.getContentOfNullable(); + if (patchedContent == null) { + Content postContent = contentService.getById(post.getId()); + postVo.setSummary(generateSummary(postContent.getContent())); + } else { + postVo.setSummary(generateSummary(patchedContent.getContent())); + } + } +} diff --git a/src/main/java/run/halo/app/service/assembler/PostAssembler.java b/src/main/java/run/halo/app/service/assembler/PostAssembler.java new file mode 100644 index 0000000000..ed02d7cd0b --- /dev/null +++ b/src/main/java/run/halo/app/service/assembler/PostAssembler.java @@ -0,0 +1,453 @@ +package run.halo.app.service.assembler; + +import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; + +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.data.domain.Page; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import run.halo.app.model.dto.post.BasePostMinimalDTO; +import run.halo.app.model.entity.Category; +import run.halo.app.model.entity.Content.PatchedContent; +import run.halo.app.model.entity.Post; +import run.halo.app.model.entity.PostMeta; +import run.halo.app.model.entity.Tag; +import run.halo.app.model.enums.CommentStatus; +import run.halo.app.model.enums.PostPermalinkType; +import run.halo.app.model.vo.ArchiveMonthVO; +import run.halo.app.model.vo.ArchiveYearVO; +import run.halo.app.model.vo.PostDetailVO; +import run.halo.app.model.vo.PostListVO; +import run.halo.app.service.CategoryService; +import run.halo.app.service.ContentService; +import run.halo.app.service.OptionService; +import run.halo.app.service.PostCategoryService; +import run.halo.app.service.PostCommentService; +import run.halo.app.service.PostMetaService; +import run.halo.app.service.PostTagService; +import run.halo.app.service.TagService; +import run.halo.app.utils.DateUtils; +import run.halo.app.utils.ServiceUtils; + +/** + * Post assembler. + * + * @author guqing + * @date 2022-03-01 + */ +@Component +public class PostAssembler extends BasePostAssembler { + + private final PostTagService postTagService; + + private final PostCategoryService postCategoryService; + + private final PostMetaService postMetaService; + + private final PostCommentService postCommentService; + + private final TagService tagService; + + private final CategoryService categoryService; + + private final ContentService contentService; + + private final OptionService optionService; + + public PostAssembler(ContentService contentService, + OptionService optionService, PostTagService postTagService, + PostCategoryService postCategoryService, + PostMetaService postMetaService, + PostCommentService postCommentService, + TagService tagService, + CategoryService categoryService) { + super(contentService, optionService); + this.postTagService = postTagService; + this.postCategoryService = postCategoryService; + this.postMetaService = postMetaService; + this.postCommentService = postCommentService; + this.tagService = tagService; + this.categoryService = categoryService; + this.contentService = contentService; + this.optionService = optionService; + } + + @Override + public BasePostMinimalDTO convertToMinimal(Post post) { + Assert.notNull(post, "Post must not be null"); + BasePostMinimalDTO basePostMinimalDTO = new BasePostMinimalDTO().convertFrom(post); + + basePostMinimalDTO.setFullPath(buildFullPath(post)); + + return basePostMinimalDTO; + } + + @Override + public List convertToMinimal(List posts) { + if (CollectionUtils.isEmpty(posts)) { + return Collections.emptyList(); + } + + return posts.stream() + .map(this::convertToMinimal) + .collect(Collectors.toList()); + } + + /** + * Converts to detail vo. + * + * @param post post must not be null + * @return post detail vo + */ + @NonNull + public PostDetailVO convertToDetailVo(Post post) { + // List tags + List tags = postTagService.listTagsBy(post.getId()); + // List categories + List categories = postCategoryService + .listCategoriesBy(post.getId()); + // List metas + List metas = postMetaService.listBy(post.getId()); + // Convert to detail vo + return convertTo(post, tags, categories, metas); + } + + /** + * Converts to a page of detail vo. + * + * @param postPage post page must not be null + * @return a page of post detail vo + */ + public Page convertToDetailVo(Page postPage) { + Assert.notNull(postPage, "Post page must not be null"); + return postPage.map(this::convertToDetailVo); + } + + /** + * Converts to a page of post list vo. + * + * @param postPage post page must not be null + * @return a page of post list vo + */ + @NonNull + public Page convertToListVo(Page postPage) { + Assert.notNull(postPage, "Post page must not be null"); + + List posts = postPage.getContent(); + + Set postIds = ServiceUtils.fetchProperty(posts, Post::getId); + + // Get tag list map + Map> tagListMap = postTagService.listTagListMapBy(postIds); + + // Get category list map + Map> categoryListMap = postCategoryService + .listCategoryListMap(postIds); + + // Get comment count + Map commentCountMap = postCommentService.countByStatusAndPostIds( + CommentStatus.PUBLISHED, postIds); + + // Get post meta list map + Map> postMetaListMap = postMetaService.listPostMetaAsMap(postIds); + + return postPage.map(post -> { + PostListVO postListVO = new PostListVO().convertFrom(post); + + generateAndSetSummaryIfAbsent(post, postListVO); + + Optional.ofNullable(tagListMap.get(post.getId())).orElseGet(LinkedList::new); + + // Set tags + postListVO.setTags(Optional.ofNullable(tagListMap.get(post.getId())) + .orElseGet(LinkedList::new) + .stream() + .filter(Objects::nonNull) + .map(tagService::convertTo) + .collect(Collectors.toList())); + + // Set categories + postListVO.setCategories(Optional.ofNullable(categoryListMap.get(post.getId())) + .orElseGet(LinkedList::new) + .stream() + .filter(Objects::nonNull) + .map(categoryService::convertTo) + .collect(Collectors.toList())); + + // Set post metas + List metas = Optional.ofNullable(postMetaListMap.get(post.getId())) + .orElseGet(LinkedList::new); + postListVO.setMetas(postMetaService.convertToMap(metas)); + + // Set comment count + postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L)); + + postListVO.setFullPath(buildFullPath(post)); + + // Post currently drafting in process + Boolean isInProcess = contentService.draftingInProgress(post.getId()); + postListVO.setInProgress(isInProcess); + + return postListVO; + }); + } + + public List convertToListVo(List posts) { + Assert.notNull(posts, "Post page must not be null"); + + Set postIds = ServiceUtils.fetchProperty(posts, Post::getId); + + // Get tag list map + Map> tagListMap = postTagService.listTagListMapBy(postIds); + + // Get category list map + Map> categoryListMap = postCategoryService + .listCategoryListMap(postIds); + + // Get comment count + Map commentCountMap = + postCommentService.countByStatusAndPostIds(CommentStatus.PUBLISHED, postIds); + + // Get post meta list map + Map> postMetaListMap = postMetaService.listPostMetaAsMap(postIds); + + return posts.stream().map(post -> { + PostListVO postListVO = new PostListVO().convertFrom(post); + + generateAndSetSummaryIfAbsent(post, postListVO); + + Optional.ofNullable(tagListMap.get(post.getId())).orElseGet(LinkedList::new); + + // Set tags + postListVO.setTags(Optional.ofNullable(tagListMap.get(post.getId())) + .orElseGet(LinkedList::new) + .stream() + .filter(Objects::nonNull) + .map(tagService::convertTo) + .collect(Collectors.toList())); + + // Set categories + postListVO.setCategories(Optional.ofNullable(categoryListMap.get(post.getId())) + .orElseGet(LinkedList::new) + .stream() + .filter(Objects::nonNull) + .map(categoryService::convertTo) + .collect(Collectors.toList())); + + // Set post metas + List metas = Optional.ofNullable(postMetaListMap.get(post.getId())) + .orElseGet(LinkedList::new); + postListVO.setMetas(postMetaService.convertToMap(metas)); + + // Set comment count + postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L)); + + postListVO.setFullPath(buildFullPath(post)); + + return postListVO; + }).collect(Collectors.toList()); + } + + + /** + * Converts to post detail vo. + * + * @param post post must not be null + * @param tags tags + * @param categories categories + * @param postMetaList postMetaList + * @return post detail vo + */ + @NonNull + public PostDetailVO convertTo(@NonNull Post post, @Nullable List tags, + @Nullable List categories, List postMetaList) { + Assert.notNull(post, "Post must not be null"); + + // Convert to base detail vo + PostDetailVO postDetailVO = new PostDetailVO().convertFrom(post); + generateAndSetSummaryIfAbsent(post, postDetailVO); + + // Extract ids + Set tagIds = ServiceUtils.fetchProperty(tags, Tag::getId); + Set categoryIds = ServiceUtils.fetchProperty(categories, Category::getId); + Set metaIds = ServiceUtils.fetchProperty(postMetaList, PostMeta::getId); + + // Get post tag ids + postDetailVO.setTagIds(tagIds); + postDetailVO.setTags(tagService.convertTo(tags)); + + // Get post category ids + postDetailVO.setCategoryIds(categoryIds); + postDetailVO.setCategories(categoryService.convertTo(categories)); + + // Get post meta ids + postDetailVO.setMetaIds(metaIds); + postDetailVO.setMetas(postMetaService.convertTo(postMetaList)); + + postDetailVO.setCommentCount(postCommentService.countByStatusAndPostId( + CommentStatus.PUBLISHED, post.getId())); + + postDetailVO.setFullPath(buildFullPath(post)); + + PatchedContent postContent = post.getContent(); + postDetailVO.setContent(postContent.getContent()); + postDetailVO.setOriginalContent(postContent.getOriginalContent()); + + // Post currently drafting in process + Boolean inProgress = contentService.draftingInProgress(post.getId()); + postDetailVO.setInProgress(inProgress); + + return postDetailVO; + } + + + /** + * Convert to year archives + * + * @param posts posts must not be null + * @return list of ArchiveYearVO + */ + public List convertToYearArchives(List posts) { + Map> yearPostMap = new HashMap<>(8); + + posts.forEach(post -> { + Calendar calendar = DateUtils.convertTo(post.getCreateTime()); + yearPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new LinkedList<>()) + .add(post); + }); + + List archives = new LinkedList<>(); + + yearPostMap.forEach((year, postList) -> { + // Build archive + ArchiveYearVO archive = new ArchiveYearVO(); + archive.setYear(year); + archive.setPosts(convertToListVo(postList)); + + // Add archive + archives.add(archive); + }); + + // Sort this list + archives.sort(new ArchiveYearVO.ArchiveComparator()); + + return archives; + } + + /** + * Convert to month archives + * + * @param posts posts must not be null + * @return list of ArchiveMonthVO + */ + public List convertToMonthArchives(List posts) { + + Map>> yearMonthPostMap = new HashMap<>(8); + + posts.forEach(post -> { + Calendar calendar = DateUtils.convertTo(post.getCreateTime()); + + yearMonthPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new HashMap<>()) + .computeIfAbsent(calendar.get(Calendar.MONTH) + 1, + month -> new LinkedList<>()) + .add(post); + }); + + List archives = new LinkedList<>(); + + yearMonthPostMap.forEach((year, monthPostMap) -> + monthPostMap.forEach((month, postList) -> { + ArchiveMonthVO archive = new ArchiveMonthVO(); + archive.setYear(year); + archive.setMonth(month); + archive.setPosts(convertToListVo(postList)); + + archives.add(archive); + })); + + // Sort this list + archives.sort(new ArchiveMonthVO.ArchiveComparator()); + + return archives; + } + + /** + * Build post full path. + * + * @param post post + * @return full patch to access. + */ + public String buildFullPath(Post post) { + + PostPermalinkType permalinkType = optionService.getPostPermalinkType(); + + String pathSuffix = optionService.getPathSuffix(); + + String archivesPrefix = optionService.getArchivesPrefix(); + + int month = DateUtils.month(post.getCreateTime()) + 1; + + String monthString = month < 10 ? "0" + month : String.valueOf(month); + + int day = DateUtils.dayOfMonth(post.getCreateTime()); + + String dayString = day < 10 ? "0" + day : String.valueOf(day); + + StringBuilder fullPath = new StringBuilder(); + + if (optionService.isEnabledAbsolutePath()) { + fullPath.append(optionService.getBlogBaseUrl()); + } + + fullPath.append(URL_SEPARATOR); + + if (permalinkType.equals(PostPermalinkType.DEFAULT)) { + fullPath.append(archivesPrefix) + .append(URL_SEPARATOR) + .append(post.getSlug()) + .append(pathSuffix); + } else if (permalinkType.equals(PostPermalinkType.ID)) { + fullPath.append("?p=") + .append(post.getId()); + } else if (permalinkType.equals(PostPermalinkType.DATE)) { + fullPath.append(DateUtils.year(post.getCreateTime())) + .append(URL_SEPARATOR) + .append(monthString) + .append(URL_SEPARATOR) + .append(post.getSlug()) + .append(pathSuffix); + } else if (permalinkType.equals(PostPermalinkType.DAY)) { + fullPath.append(DateUtils.year(post.getCreateTime())) + .append(URL_SEPARATOR) + .append(monthString) + .append(URL_SEPARATOR) + .append(dayString) + .append(URL_SEPARATOR) + .append(post.getSlug()) + .append(pathSuffix); + } else if (permalinkType.equals(PostPermalinkType.YEAR)) { + fullPath.append(DateUtils.year(post.getCreateTime())) + .append(URL_SEPARATOR) + .append(post.getSlug()) + .append(pathSuffix); + } else if (permalinkType.equals(PostPermalinkType.ID_SLUG)) { + fullPath.append(archivesPrefix) + .append(URL_SEPARATOR) + .append(post.getId()) + .append(pathSuffix); + } + return fullPath.toString(); + } +} diff --git a/src/main/java/run/halo/app/service/assembler/PostRenderAssembler.java b/src/main/java/run/halo/app/service/assembler/PostRenderAssembler.java new file mode 100644 index 0000000000..e4e24dfb94 --- /dev/null +++ b/src/main/java/run/halo/app/service/assembler/PostRenderAssembler.java @@ -0,0 +1,82 @@ +package run.halo.app.service.assembler; + +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import run.halo.app.model.entity.Content; +import run.halo.app.model.entity.Content.PatchedContent; +import run.halo.app.model.entity.Post; +import run.halo.app.model.vo.PostDetailVO; +import run.halo.app.service.CategoryService; +import run.halo.app.service.ContentPatchLogService; +import run.halo.app.service.ContentService; +import run.halo.app.service.OptionService; +import run.halo.app.service.PostCategoryService; +import run.halo.app.service.PostCommentService; +import run.halo.app.service.PostMetaService; +import run.halo.app.service.PostTagService; +import run.halo.app.service.TagService; + +/** + * Post assembler for theme render. + * + * @author guqing + * @date 2022-03-01 + */ +@Component +public class PostRenderAssembler extends PostAssembler { + + private final ContentService contentService; + private final ContentPatchLogService contentPatchLogService; + + public PostRenderAssembler(ContentService contentService, + OptionService optionService, + PostTagService postTagService, + PostCategoryService postCategoryService, + PostMetaService postMetaService, + PostCommentService postCommentService, + TagService tagService, + CategoryService categoryService, + ContentPatchLogService contentPatchLogService) { + super(contentService, optionService, postTagService, postCategoryService, postMetaService, + postCommentService, tagService, categoryService); + this.contentService = contentService; + this.contentPatchLogService = contentPatchLogService; + } + + @Override + public Page convertToDetailVo(Page postPage) { + Assert.notNull(postPage, "Post page must not be null"); + // Populate post content + postPage.getContent().forEach(post -> { + Content postContent = contentService.getById(post.getId()); + post.setContent(Content.PatchedContent.of(postContent)); + }); + return postPage.map(this::convertToDetailVo); + } + + @Override + public PostDetailVO convertToDetailVo(Post post) { + Assert.notNull(post, "The post must not be null."); + post.setContent(PatchedContent.of(contentService.getById(post.getId()))); + return super.convertToDetailVo(post); + } + + /** + * Gets for preview. + * + * @param post post + * @return post detail with latest patched content. + */ + public PostDetailVO convertToPreviewDetailVo(Post post) { + Assert.notNull(post, "The post must not be null."); + post.setContent(getLatestContentBy(post.getId())); + return super.convertToDetailVo(post); + } + + private PatchedContent getLatestContentBy(Integer postId) { + Content postContent = contentService.getById(postId); + // Use the head pointer stored in the post content. + return contentPatchLogService.getPatchedContentById(postContent.getHeadPatchLogId()); + } +} diff --git a/src/main/java/run/halo/app/service/assembler/SheetAssembler.java b/src/main/java/run/halo/app/service/assembler/SheetAssembler.java new file mode 100644 index 0000000000..0a05c963ed --- /dev/null +++ b/src/main/java/run/halo/app/service/assembler/SheetAssembler.java @@ -0,0 +1,185 @@ +package run.halo.app.service.assembler; + +import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.data.domain.Page; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import run.halo.app.model.dto.post.BasePostMinimalDTO; +import run.halo.app.model.entity.Content.PatchedContent; +import run.halo.app.model.entity.Sheet; +import run.halo.app.model.entity.SheetMeta; +import run.halo.app.model.enums.CommentStatus; +import run.halo.app.model.enums.SheetPermalinkType; +import run.halo.app.model.vo.SheetDetailVO; +import run.halo.app.model.vo.SheetListVO; +import run.halo.app.service.ContentService; +import run.halo.app.service.OptionService; +import run.halo.app.service.SheetCommentService; +import run.halo.app.service.SheetMetaService; +import run.halo.app.utils.ServiceUtils; + +/** + * Sheet assembler. + * + * @author guqing + * @date 2022-03-01 + */ +@Component +public class SheetAssembler extends BasePostAssembler { + + private final SheetCommentService sheetCommentService; + + private final ContentService contentService; + + private final SheetMetaService sheetMetaService; + + private final OptionService optionService; + + public SheetAssembler(SheetCommentService sheetCommentService, + ContentService contentService, + SheetMetaService sheetMetaService, + OptionService optionService) { + super(contentService, optionService); + this.sheetCommentService = sheetCommentService; + this.contentService = contentService; + this.sheetMetaService = sheetMetaService; + this.optionService = optionService; + } + + /** + * Converts to list dto page. + * + * @param sheetPage sheet page must not be nulls + * @return a page of sheet list dto + */ + @NonNull + public Page convertToListVo(Page sheetPage) { + Assert.notNull(sheetPage, "Sheet page must not be null"); + + // Get all sheet id + List sheets = sheetPage.getContent(); + + Set sheetIds = ServiceUtils.fetchProperty(sheets, Sheet::getId); + + // key: sheet id, value: comment count + Map sheetCommentCountMap = sheetCommentService.countByStatusAndPostIds( + CommentStatus.PUBLISHED, sheetIds); + + return sheetPage.map(sheet -> { + SheetListVO sheetListVO = new SheetListVO().convertFrom(sheet); + sheetListVO.setCommentCount(sheetCommentCountMap.getOrDefault(sheet.getId(), 0L)); + + sheetListVO.setFullPath(buildFullPath(sheet)); + + // Post currently drafting in process + Boolean isInProcess = contentService.draftingInProgress(sheet.getId()); + sheetListVO.setInProgress(isInProcess); + + return sheetListVO; + }); + } + + /** + * Converts to detail vo. + * + * @param sheet sheet must not be null + * @return sheet detail vo + */ + @NonNull + public SheetDetailVO convertToDetailVo(Sheet sheet) { + // List metas + List metas = sheetMetaService.listBy(sheet.getId()); + // Convert to detail vo + return convertTo(sheet, metas); + } + + @Override + public BasePostMinimalDTO convertToMinimal(Sheet sheet) { + Assert.notNull(sheet, "Sheet must not be null"); + BasePostMinimalDTO basePostMinimalDTO = new BasePostMinimalDTO().convertFrom(sheet); + + basePostMinimalDTO.setFullPath(buildFullPath(sheet)); + + return basePostMinimalDTO; + } + + @Override + public List convertToMinimal(List sheets) { + if (CollectionUtils.isEmpty(sheets)) { + return Collections.emptyList(); + } + + return sheets.stream() + .map(this::convertToMinimal) + .collect(Collectors.toList()); + } + + @NonNull + public SheetDetailVO convertTo(@NonNull Sheet sheet, List metas) { + Assert.notNull(sheet, "Sheet must not be null"); + + // Convert to base detail vo + SheetDetailVO sheetDetailVO = new SheetDetailVO().convertFrom(sheet); + + Set metaIds = ServiceUtils.fetchProperty(metas, SheetMeta::getId); + + // Get sheet meta ids + sheetDetailVO.setMetaIds(metaIds); + sheetDetailVO.setMetas(sheetMetaService.convertTo(metas)); + + generateAndSetSummaryIfAbsent(sheet, sheetDetailVO); + + sheetDetailVO.setCommentCount(sheetCommentService.countByStatusAndPostId( + CommentStatus.PUBLISHED, sheet.getId())); + + sheetDetailVO.setFullPath(buildFullPath(sheet)); + + PatchedContent sheetContent = sheet.getContent(); + sheetDetailVO.setContent(sheetContent.getContent()); + sheetDetailVO.setOriginalContent(sheetContent.getOriginalContent()); + + // Sheet currently drafting in process + Boolean inProgress = contentService.draftingInProgress(sheet.getId()); + sheetDetailVO.setInProgress(inProgress); + + return sheetDetailVO; + } + + /** + * Build sheet full path. + * + * @param sheet sheet + * @return a full path to access. + */ + private String buildFullPath(Sheet sheet) { + StringBuilder fullPath = new StringBuilder(); + + SheetPermalinkType permalinkType = optionService.getSheetPermalinkType(); + + if (optionService.isEnabledAbsolutePath()) { + fullPath.append(optionService.getBlogBaseUrl()); + } + + if (permalinkType.equals(SheetPermalinkType.SECONDARY)) { + fullPath.append(URL_SEPARATOR) + .append(optionService.getSheetPrefix()) + .append(URL_SEPARATOR) + .append(sheet.getSlug()) + .append(optionService.getPathSuffix()); + } else if (permalinkType.equals(SheetPermalinkType.ROOT)) { + fullPath.append(URL_SEPARATOR) + .append(sheet.getSlug()) + .append(optionService.getPathSuffix()); + } + + return fullPath.toString(); + } +} diff --git a/src/main/java/run/halo/app/service/assembler/SheetRenderAssembler.java b/src/main/java/run/halo/app/service/assembler/SheetRenderAssembler.java new file mode 100644 index 0000000000..ecb2b6588b --- /dev/null +++ b/src/main/java/run/halo/app/service/assembler/SheetRenderAssembler.java @@ -0,0 +1,68 @@ +package run.halo.app.service.assembler; + +import java.util.List; +import org.springframework.stereotype.Component; +import run.halo.app.model.entity.Content; +import run.halo.app.model.entity.Content.PatchedContent; +import run.halo.app.model.entity.Sheet; +import run.halo.app.model.entity.SheetMeta; +import run.halo.app.model.vo.SheetDetailVO; +import run.halo.app.service.ContentPatchLogService; +import run.halo.app.service.ContentService; +import run.halo.app.service.OptionService; +import run.halo.app.service.SheetCommentService; +import run.halo.app.service.SheetMetaService; + +/** + * Sheet assembler for theme render. + * + * @author guqing + * @date 2022-03-01 + */ +@Component +public class SheetRenderAssembler extends SheetAssembler { + + private final SheetMetaService sheetMetaService; + private final ContentService contentService; + private final ContentPatchLogService contentPatchLogService; + + public SheetRenderAssembler(SheetCommentService sheetCommentService, + ContentService contentService, + SheetMetaService sheetMetaService, + OptionService optionService, + ContentPatchLogService contentPatchLogService) { + super(sheetCommentService, contentService, sheetMetaService, optionService); + this.sheetMetaService = sheetMetaService; + this.contentService = contentService; + this.contentPatchLogService = contentPatchLogService; + } + + @Override + public SheetDetailVO convertToDetailVo(Sheet sheet) { + sheet.setContent(Content.PatchedContent.of(contentService.getById(sheet.getId()))); + // List metas + List metas = sheetMetaService.listBy(sheet.getId()); + // Convert to detail vo + return super.convertTo(sheet, metas); + } + + /** + * Gets sheet detail to preview. + * + * @param sheet sheet + * @return sheet detail with the latest patched content. + */ + public SheetDetailVO convertToPreviewDetailVo(Sheet sheet) { + sheet.setContent(getLatestContentBy(sheet.getId())); + // List metas + List metas = sheetMetaService.listBy(sheet.getId()); + // Convert to detail vo + return super.convertTo(sheet, metas); + } + + private PatchedContent getLatestContentBy(Integer postId) { + Content postContent = contentService.getById(postId); + // Use the head pointer stored in the post content. + return contentPatchLogService.getPatchedContentById(postContent.getHeadPatchLogId()); + } +} diff --git a/src/main/java/run/halo/app/service/base/BasePostService.java b/src/main/java/run/halo/app/service/base/BasePostService.java index c56aa2c890..6ca0e95a41 100644 --- a/src/main/java/run/halo/app/service/base/BasePostService.java +++ b/src/main/java/run/halo/app/service/base/BasePostService.java @@ -6,9 +6,6 @@ import org.springframework.data.domain.Pageable; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; -import run.halo.app.model.dto.post.BasePostDetailDTO; -import run.halo.app.model.dto.post.BasePostMinimalDTO; -import run.halo.app.model.dto.post.BasePostSimpleDTO; import run.halo.app.model.entity.BasePost; import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Content.PatchedContent; @@ -224,69 +221,6 @@ public interface BasePostService extends CrudService convertToMinimal(@Nullable List posts); - - /** - * Convert page of POST to minimal dto of page. - * - * @param postPage postPage must not be null. - * @return a page of minimal dto. - */ - @NonNull - Page convertToMinimal(@NonNull Page postPage); - - /** - * Convert POST to simple dto. - * - * @param post post must not be null. - * @return simple dto. - */ - @NonNull - BasePostSimpleDTO convertToSimple(@NonNull POST post); - - /** - * Convert list of POST to list of simple dto. - * - * @param posts posts must not be null. - * @return a list of simple dto. - */ - @NonNull - List convertToSimple(@Nullable List posts); - - /** - * Convert page of POST to page of simple dto. - * - * @param postPage postPage must not be null. - * @return a page of simple dto. - */ - @NonNull - Page convertToSimple(@NonNull Page postPage); - - /** - * Convert POST to detail dto. - * - * @param post post must not be null. - * @return detail dto. - */ - @NonNull - BasePostDetailDTO convertToDetail(@NonNull POST post); - /** * Updates draft content. * diff --git a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java index 571ec95a6f..70643d7f23 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -24,8 +24,6 @@ import run.halo.app.exception.BadRequestException; import run.halo.app.exception.NotFoundException; import run.halo.app.exception.ServiceException; -import run.halo.app.model.dto.post.BasePostDetailDTO; -import run.halo.app.model.dto.post.BasePostMinimalDTO; import run.halo.app.model.dto.post.BasePostSimpleDTO; import run.halo.app.model.entity.BasePost; import run.halo.app.model.entity.Content; @@ -303,7 +301,6 @@ public POST createOrUpdateBy(POST post) { PatchedContent postContent = post.getContent(); // word count stat post.setWordCount(htmlFormatWordCount(postContent.getContent())); - post.setContent(postContent); POST savedPost; // Create or update post @@ -329,78 +326,6 @@ public POST createOrUpdateBy(POST post) { return savedPost; } - @Override - public BasePostMinimalDTO convertToMinimal(POST post) { - Assert.notNull(post, "Post must not be null"); - - return new BasePostMinimalDTO().convertFrom(post); - } - - @Override - public List convertToMinimal(List posts) { - if (CollectionUtils.isEmpty(posts)) { - return Collections.emptyList(); - } - - return posts.stream() - .map(this::convertToMinimal) - .collect(Collectors.toList()); - } - - @Override - public Page convertToMinimal(Page postPage) { - Assert.notNull(postPage, "Post page must not be null"); - - return postPage.map(this::convertToMinimal); - } - - @Override - public BasePostSimpleDTO convertToSimple(POST post) { - Assert.notNull(post, "Post must not be null"); - - BasePostSimpleDTO basePostSimpleDTO = new BasePostSimpleDTO().convertFrom(post); - - // Set summary - generateAndSetSummaryIfAbsent(post, basePostSimpleDTO); - - // Post currently drafting in process - Boolean isInProcess = contentService.draftingInProgress(post.getId()); - basePostSimpleDTO.setInProgress(isInProcess); - - return basePostSimpleDTO; - } - - @Override - public List convertToSimple(List posts) { - if (CollectionUtils.isEmpty(posts)) { - return Collections.emptyList(); - } - - return posts.stream() - .map(this::convertToSimple) - .collect(Collectors.toList()); - } - - @Override - public Page convertToSimple(Page postPage) { - Assert.notNull(postPage, "Post page must not be null"); - - return postPage.map(this::convertToSimple); - } - - @Override - public BasePostDetailDTO convertToDetail(POST post) { - Assert.notNull(post, "Post must not be null"); - - BasePostDetailDTO postDetail = new BasePostDetailDTO().convertFrom(post); - - // Post currently drafting in process - Boolean isInProcess = contentService.draftingInProgress(post.getId()); - postDetail.setInProgress(isInProcess); - - return postDetail; - } - @Override @Transactional(rollbackFor = Exception.class) public POST updateDraftContent(String content, String originalContent, Integer postId) { diff --git a/src/main/java/run/halo/app/service/impl/PostServiceImpl.java b/src/main/java/run/halo/app/service/impl/PostServiceImpl.java index a3ce877988..52edf9cb7b 100644 --- a/src/main/java/run/halo/app/service/impl/PostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/PostServiceImpl.java @@ -1,18 +1,14 @@ package run.halo.app.service.impl; import static org.springframework.data.domain.Sort.Direction.DESC; -import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -29,7 +25,6 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -38,8 +33,6 @@ import run.halo.app.event.post.PostUpdatedEvent; import run.halo.app.event.post.PostVisitEvent; import run.halo.app.exception.NotFoundException; -import run.halo.app.model.dto.post.BasePostMinimalDTO; -import run.halo.app.model.dto.post.BasePostSimpleDTO; import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Content.PatchedContent; @@ -49,9 +42,7 @@ import run.halo.app.model.entity.PostMeta; import run.halo.app.model.entity.PostTag; import run.halo.app.model.entity.Tag; -import run.halo.app.model.enums.CommentStatus; import run.halo.app.model.enums.LogType; -import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.params.PostParam; import run.halo.app.model.params.PostQuery; @@ -59,7 +50,6 @@ import run.halo.app.model.vo.ArchiveMonthVO; import run.halo.app.model.vo.ArchiveYearVO; import run.halo.app.model.vo.PostDetailVO; -import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostMarkdownVO; import run.halo.app.repository.PostRepository; import run.halo.app.repository.base.BasePostRepository; @@ -73,6 +63,7 @@ import run.halo.app.service.PostService; import run.halo.app.service.PostTagService; import run.halo.app.service.TagService; +import run.halo.app.service.assembler.PostAssembler; import run.halo.app.utils.DateUtils; import run.halo.app.utils.HaloUtils; import run.halo.app.utils.MarkdownUtils; @@ -94,6 +85,8 @@ @Service public class PostServiceImpl extends BasePostServiceImpl implements PostService { + private final PostAssembler postAssembler; + private final PostRepository postRepository; private final TagService tagService; @@ -119,7 +112,7 @@ public class PostServiceImpl extends BasePostServiceImpl implements PostSe private final ApplicationContext applicationContext; public PostServiceImpl(BasePostRepository basePostRepository, - OptionService optionService, + PostAssembler postAssembler, OptionService optionService, PostRepository postRepository, TagService tagService, CategoryService categoryService, @@ -132,6 +125,7 @@ public PostServiceImpl(BasePostRepository basePostRepository, ContentPatchLogService contentPatchLogService, ApplicationContext applicationContext) { super(basePostRepository, optionService, contentService, contentPatchLogService); + this.postAssembler = postAssembler; this.postRepository = postRepository; this.tagService = tagService; this.categoryService = categoryService; @@ -315,7 +309,7 @@ public List listYearArchives() { List posts = postRepository .findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime")); - return convertToYearArchives(posts); + return postAssembler.convertToYearArchives(posts); } @Override @@ -324,67 +318,7 @@ public List listMonthArchives() { List posts = postRepository .findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime")); - return convertToMonthArchives(posts); - } - - @Override - public List convertToYearArchives(List posts) { - Map> yearPostMap = new HashMap<>(8); - - posts.forEach(post -> { - Calendar calendar = DateUtils.convertTo(post.getCreateTime()); - yearPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new LinkedList<>()) - .add(post); - }); - - List archives = new LinkedList<>(); - - yearPostMap.forEach((year, postList) -> { - // Build archive - ArchiveYearVO archive = new ArchiveYearVO(); - archive.setYear(year); - archive.setPosts(convertToListVo(postList)); - - // Add archive - archives.add(archive); - }); - - // Sort this list - archives.sort(new ArchiveYearVO.ArchiveComparator()); - - return archives; - } - - @Override - public List convertToMonthArchives(List posts) { - - Map>> yearMonthPostMap = new HashMap<>(8); - - posts.forEach(post -> { - Calendar calendar = DateUtils.convertTo(post.getCreateTime()); - - yearMonthPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new HashMap<>()) - .computeIfAbsent(calendar.get(Calendar.MONTH) + 1, - month -> new LinkedList<>()) - .add(post); - }); - - List archives = new LinkedList<>(); - - yearMonthPostMap.forEach((year, monthPostMap) -> - monthPostMap.forEach((month, postList) -> { - ArchiveMonthVO archive = new ArchiveMonthVO(); - archive.setYear(year); - archive.setMonth(month); - archive.setPosts(convertToListVo(postList)); - - archives.add(archive); - })); - - // Sort this list - archives.sort(new ArchiveMonthVO.ArchiveComparator()); - - return archives; + return postAssembler.convertToMonthArchives(posts); } @Override @@ -555,30 +489,6 @@ public String exportMarkdown(Post post) { return content.toString(); } - @Override - public PostDetailVO convertToDetailVo(Post post) { - return convertToDetailVo(post, false); - } - - @Override - public PostDetailVO convertToDetailVo(Post post, boolean queryEncryptCategory) { - // List tags - List tags = postTagService.listTagsBy(post.getId()); - // List categories - List categories = postCategoryService - .listCategoriesBy(post.getId()); - // List metas - List metas = postMetaService.listBy(post.getId()); - // Convert to detail vo - return convertTo(post, tags, categories, metas); - } - - @Override - public Page convertToDetailVo(Page postPage) { - Assert.notNull(postPage, "Post page must not be null"); - return postPage.map(this::convertToDetailVo); - } - @Override public Post removeById(Integer postId) { Assert.notNull(postId, "Post id must not be null"); @@ -616,217 +526,6 @@ public Post removeById(Integer postId) { return deletedPost; } - @Override - public Page convertToListVo(Page postPage) { - return convertToListVo(postPage, false); - } - - @Override - public Page convertToListVo(Page postPage, boolean queryEncryptCategory) { - Assert.notNull(postPage, "Post page must not be null"); - - List posts = postPage.getContent(); - - Set postIds = ServiceUtils.fetchProperty(posts, Post::getId); - - // Get tag list map - Map> tagListMap = postTagService.listTagListMapBy(postIds); - - // Get category list map - Map> categoryListMap = postCategoryService - .listCategoryListMap(postIds); - - // Get comment count - Map commentCountMap = postCommentService.countByStatusAndPostIds( - CommentStatus.PUBLISHED, postIds); - - // Get post meta list map - Map> postMetaListMap = postMetaService.listPostMetaAsMap(postIds); - - return postPage.map(post -> { - PostListVO postListVO = new PostListVO().convertFrom(post); - - generateAndSetSummaryIfAbsent(post, postListVO); - - Optional.ofNullable(tagListMap.get(post.getId())).orElseGet(LinkedList::new); - - // Set tags - postListVO.setTags(Optional.ofNullable(tagListMap.get(post.getId())) - .orElseGet(LinkedList::new) - .stream() - .filter(Objects::nonNull) - .map(tagService::convertTo) - .collect(Collectors.toList())); - - // Set categories - postListVO.setCategories(Optional.ofNullable(categoryListMap.get(post.getId())) - .orElseGet(LinkedList::new) - .stream() - .filter(Objects::nonNull) - .map(categoryService::convertTo) - .collect(Collectors.toList())); - - // Set post metas - List metas = Optional.ofNullable(postMetaListMap.get(post.getId())) - .orElseGet(LinkedList::new); - postListVO.setMetas(postMetaService.convertToMap(metas)); - - // Set comment count - postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L)); - - postListVO.setFullPath(buildFullPath(post)); - - // Post currently drafting in process - Boolean isInProcess = postContentService.draftingInProgress(post.getId()); - postListVO.setInProgress(isInProcess); - - return postListVO; - }); - } - - @Override - public List convertToListVo(List posts) { - Assert.notNull(posts, "Post page must not be null"); - - Set postIds = ServiceUtils.fetchProperty(posts, Post::getId); - - // Get tag list map - Map> tagListMap = postTagService.listTagListMapBy(postIds); - - // Get category list map - Map> categoryListMap = postCategoryService - .listCategoryListMap(postIds); - - // Get comment count - Map commentCountMap = - postCommentService.countByStatusAndPostIds(CommentStatus.PUBLISHED, postIds); - - // Get post meta list map - Map> postMetaListMap = postMetaService.listPostMetaAsMap(postIds); - - return posts.stream().map(post -> { - PostListVO postListVO = new PostListVO().convertFrom(post); - - generateAndSetSummaryIfAbsent(post, postListVO); - - Optional.ofNullable(tagListMap.get(post.getId())).orElseGet(LinkedList::new); - - // Set tags - postListVO.setTags(Optional.ofNullable(tagListMap.get(post.getId())) - .orElseGet(LinkedList::new) - .stream() - .filter(Objects::nonNull) - .map(tagService::convertTo) - .collect(Collectors.toList())); - - // Set categories - postListVO.setCategories(Optional.ofNullable(categoryListMap.get(post.getId())) - .orElseGet(LinkedList::new) - .stream() - .filter(Objects::nonNull) - .map(categoryService::convertTo) - .collect(Collectors.toList())); - - // Set post metas - List metas = Optional.ofNullable(postMetaListMap.get(post.getId())) - .orElseGet(LinkedList::new); - postListVO.setMetas(postMetaService.convertToMap(metas)); - - // Set comment count - postListVO.setCommentCount(commentCountMap.getOrDefault(post.getId(), 0L)); - - postListVO.setFullPath(buildFullPath(post)); - - return postListVO; - }).collect(Collectors.toList()); - } - - @Override - public BasePostMinimalDTO convertToMinimal(Post post) { - Assert.notNull(post, "Post must not be null"); - BasePostMinimalDTO basePostMinimalDTO = new BasePostMinimalDTO().convertFrom(post); - - basePostMinimalDTO.setFullPath(buildFullPath(post)); - - return basePostMinimalDTO; - } - - @Override - public List convertToMinimal(List posts) { - if (CollectionUtils.isEmpty(posts)) { - return Collections.emptyList(); - } - - return posts.stream() - .map(this::convertToMinimal) - .collect(Collectors.toList()); - } - - @Override - public BasePostSimpleDTO convertToSimple(Post post) { - Assert.notNull(post, "Post must not be null"); - - BasePostSimpleDTO basePostSimpleDTO = new BasePostSimpleDTO().convertFrom(post); - - // Set summary - generateAndSetSummaryIfAbsent(post, basePostSimpleDTO); - - basePostSimpleDTO.setFullPath(buildFullPath(post)); - - return basePostSimpleDTO; - } - - /** - * Converts to post detail vo. - * - * @param post post must not be null - * @param tags tags - * @param categories categories - * @param postMetaList postMetaList - * @return post detail vo - */ - @NonNull - private PostDetailVO convertTo(@NonNull Post post, @Nullable List tags, - @Nullable List categories, List postMetaList) { - Assert.notNull(post, "Post must not be null"); - - // Convert to base detail vo - PostDetailVO postDetailVO = new PostDetailVO().convertFrom(post); - generateAndSetSummaryIfAbsent(post, postDetailVO); - - // Extract ids - Set tagIds = ServiceUtils.fetchProperty(tags, Tag::getId); - Set categoryIds = ServiceUtils.fetchProperty(categories, Category::getId); - Set metaIds = ServiceUtils.fetchProperty(postMetaList, PostMeta::getId); - - // Get post tag ids - postDetailVO.setTagIds(tagIds); - postDetailVO.setTags(tagService.convertTo(tags)); - - // Get post category ids - postDetailVO.setCategoryIds(categoryIds); - postDetailVO.setCategories(categoryService.convertTo(categories)); - - // Get post meta ids - postDetailVO.setMetaIds(metaIds); - postDetailVO.setMetas(postMetaService.convertTo(postMetaList)); - - postDetailVO.setCommentCount(postCommentService.countByStatusAndPostId( - CommentStatus.PUBLISHED, post.getId())); - - postDetailVO.setFullPath(buildFullPath(post)); - - PatchedContent postContent = post.getContent(); - postDetailVO.setContent(postContent.getContent()); - postDetailVO.setOriginalContent(postContent.getOriginalContent()); - - // Post currently drafting in process - Boolean inProgress = postContentService.draftingInProgress(post.getId()); - postDetailVO.setInProgress(inProgress); - - return postDetailVO; - } - /** * Build specification by post query. * @@ -926,7 +625,7 @@ private PostDetailVO createOrUpdate(@NonNull Post post, Set tagIds, post.setContent( postContentPatchLogService.getPatchedContentById(postContent.getHeadPatchLogId())); // Convert to post detail vo - return convertTo(post, tags, categories, postMetaList); + return postAssembler.convertTo(post, tags, categories, postMetaList); } @Override @@ -960,7 +659,7 @@ private PostMarkdownVO convertToPostMarkdownVo(Post post) { frontMatter.append("updated: ").append(post.getUpdateTime()).append("\n"); //set fullPath - frontMatter.append("url: ").append(buildFullPath(post)).append("\n"); + frontMatter.append("url: ").append(postAssembler.buildFullPath(post)).append("\n"); //set category List categories = postCategoryService.listCategoriesBy(post.getId()); @@ -998,66 +697,4 @@ private PostMarkdownVO convertToPostMarkdownVo(Post post) { postMarkdownVO.setSlug(post.getSlug()); return postMarkdownVO; } - - private String buildFullPath(Post post) { - - PostPermalinkType permalinkType = optionService.getPostPermalinkType(); - - String pathSuffix = optionService.getPathSuffix(); - - String archivesPrefix = optionService.getArchivesPrefix(); - - int month = DateUtils.month(post.getCreateTime()) + 1; - - String monthString = month < 10 ? "0" + month : String.valueOf(month); - - int day = DateUtils.dayOfMonth(post.getCreateTime()); - - String dayString = day < 10 ? "0" + day : String.valueOf(day); - - StringBuilder fullPath = new StringBuilder(); - - if (optionService.isEnabledAbsolutePath()) { - fullPath.append(optionService.getBlogBaseUrl()); - } - - fullPath.append(URL_SEPARATOR); - - if (permalinkType.equals(PostPermalinkType.DEFAULT)) { - fullPath.append(archivesPrefix) - .append(URL_SEPARATOR) - .append(post.getSlug()) - .append(pathSuffix); - } else if (permalinkType.equals(PostPermalinkType.ID)) { - fullPath.append("?p=") - .append(post.getId()); - } else if (permalinkType.equals(PostPermalinkType.DATE)) { - fullPath.append(DateUtils.year(post.getCreateTime())) - .append(URL_SEPARATOR) - .append(monthString) - .append(URL_SEPARATOR) - .append(post.getSlug()) - .append(pathSuffix); - } else if (permalinkType.equals(PostPermalinkType.DAY)) { - fullPath.append(DateUtils.year(post.getCreateTime())) - .append(URL_SEPARATOR) - .append(monthString) - .append(URL_SEPARATOR) - .append(dayString) - .append(URL_SEPARATOR) - .append(post.getSlug()) - .append(pathSuffix); - } else if (permalinkType.equals(PostPermalinkType.YEAR)) { - fullPath.append(DateUtils.year(post.getCreateTime())) - .append(URL_SEPARATOR) - .append(post.getSlug()) - .append(pathSuffix); - } else if (permalinkType.equals(PostPermalinkType.ID_SLUG)) { - fullPath.append(archivesPrefix) - .append(URL_SEPARATOR) - .append(post.getId()) - .append(pathSuffix); - } - return fullPath.toString(); - } } diff --git a/src/main/java/run/halo/app/service/impl/SheetServiceImpl.java b/src/main/java/run/halo/app/service/impl/SheetServiceImpl.java index 243960a08f..4f5093f95d 100644 --- a/src/main/java/run/halo/app/service/impl/SheetServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/SheetServiceImpl.java @@ -1,39 +1,28 @@ package run.halo.app.service.impl; -import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; - import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.lang.NonNull; import org.springframework.stereotype.Service; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; import run.halo.app.event.logger.LogEvent; import run.halo.app.event.post.SheetVisitEvent; import run.halo.app.exception.AlreadyExistsException; import run.halo.app.exception.NotFoundException; import run.halo.app.model.dto.IndependentSheetDTO; -import run.halo.app.model.dto.post.BasePostMinimalDTO; import run.halo.app.model.entity.Content; import run.halo.app.model.entity.Content.PatchedContent; import run.halo.app.model.entity.Sheet; import run.halo.app.model.entity.SheetComment; import run.halo.app.model.entity.SheetMeta; -import run.halo.app.model.enums.CommentStatus; import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.PostStatus; -import run.halo.app.model.enums.SheetPermalinkType; -import run.halo.app.model.vo.SheetDetailVO; -import run.halo.app.model.vo.SheetListVO; import run.halo.app.repository.SheetRepository; import run.halo.app.service.ContentPatchLogService; import run.halo.app.service.ContentService; @@ -294,98 +283,11 @@ public Sheet removeById(Integer id) { return sheet; } - @Override - public Page convertToListVo(Page sheetPage) { - Assert.notNull(sheetPage, "Sheet page must not be null"); - - // Get all sheet id - List sheets = sheetPage.getContent(); - - Set sheetIds = ServiceUtils.fetchProperty(sheets, Sheet::getId); - - // key: sheet id, value: comment count - Map sheetCommentCountMap = sheetCommentService.countByStatusAndPostIds( - CommentStatus.PUBLISHED, sheetIds); - - return sheetPage.map(sheet -> { - SheetListVO sheetListVO = new SheetListVO().convertFrom(sheet); - sheetListVO.setCommentCount(sheetCommentCountMap.getOrDefault(sheet.getId(), 0L)); - - sheetListVO.setFullPath(buildFullPath(sheet)); - - // Post currently drafting in process - Boolean isInProcess = sheetContentService.draftingInProgress(sheet.getId()); - sheetListVO.setInProgress(isInProcess); - - return sheetListVO; - }); - } - @Override public void publishVisitEvent(Integer sheetId) { eventPublisher.publishEvent(new SheetVisitEvent(this, sheetId)); } - @Override - public SheetDetailVO convertToDetailVo(Sheet sheet) { - // List metas - List metas = sheetMetaService.listBy(sheet.getId()); - // Convert to detail vo - return convertTo(sheet, metas); - } - - @Override - public BasePostMinimalDTO convertToMinimal(Sheet sheet) { - Assert.notNull(sheet, "Sheet must not be null"); - BasePostMinimalDTO basePostMinimalDTO = new BasePostMinimalDTO().convertFrom(sheet); - - basePostMinimalDTO.setFullPath(buildFullPath(sheet)); - - return basePostMinimalDTO; - } - - @Override - public List convertToMinimal(List sheets) { - if (CollectionUtils.isEmpty(sheets)) { - return Collections.emptyList(); - } - - return sheets.stream() - .map(this::convertToMinimal) - .collect(Collectors.toList()); - } - - @NonNull - private SheetDetailVO convertTo(@NonNull Sheet sheet, List metas) { - Assert.notNull(sheet, "Sheet must not be null"); - - // Convert to base detail vo - SheetDetailVO sheetDetailVO = new SheetDetailVO().convertFrom(sheet); - - Set metaIds = ServiceUtils.fetchProperty(metas, SheetMeta::getId); - - // Get sheet meta ids - sheetDetailVO.setMetaIds(metaIds); - sheetDetailVO.setMetas(sheetMetaService.convertTo(metas)); - - generateAndSetSummaryIfAbsent(sheet, sheetDetailVO); - - sheetDetailVO.setCommentCount(sheetCommentService.countByStatusAndPostId( - CommentStatus.PUBLISHED, sheet.getId())); - - sheetDetailVO.setFullPath(buildFullPath(sheet)); - - PatchedContent sheetContent = sheet.getContent(); - sheetDetailVO.setContent(sheetContent.getContent()); - sheetDetailVO.setOriginalContent(sheetContent.getOriginalContent()); - - // Sheet currently drafting in process - Boolean inProgress = sheetContentService.draftingInProgress(sheet.getId()); - sheetDetailVO.setInProgress(inProgress); - - return sheetDetailVO; - } - @Override protected void slugMustNotExist(Sheet sheet) { Assert.notNull(sheet, "Sheet must not be null"); @@ -405,29 +307,4 @@ protected void slugMustNotExist(Sheet sheet) { throw new AlreadyExistsException("页面别名 " + sheet.getSlug() + " 已存在"); } } - - private String buildFullPath(Sheet sheet) { - StringBuilder fullPath = new StringBuilder(); - - SheetPermalinkType permalinkType = optionService.getSheetPermalinkType(); - - if (optionService.isEnabledAbsolutePath()) { - fullPath.append(optionService.getBlogBaseUrl()); - } - - if (permalinkType.equals(SheetPermalinkType.SECONDARY)) { - fullPath.append(URL_SEPARATOR) - .append(optionService.getSheetPrefix()) - .append(URL_SEPARATOR) - .append(sheet.getSlug()) - .append(optionService.getPathSuffix()); - } else if (permalinkType.equals(SheetPermalinkType.ROOT)) { - fullPath.append(URL_SEPARATOR) - .append(sheet.getSlug()) - .append(optionService.getPathSuffix()); - } - - return fullPath.toString(); - } - } diff --git a/src/test/java/run/halo/app/service/impl/CategoryServiceTest.java b/src/test/java/run/halo/app/service/impl/CategoryServiceTest.java index 883dfe1cd7..552550d300 100644 --- a/src/test/java/run/halo/app/service/impl/CategoryServiceTest.java +++ b/src/test/java/run/halo/app/service/impl/CategoryServiceTest.java @@ -1,15 +1,22 @@ package run.halo.app.service.impl; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.List; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; import run.halo.app.model.entity.Category; import run.halo.app.model.vo.CategoryVO; +import run.halo.app.repository.CategoryRepository; +import run.halo.app.service.OptionService; +import run.halo.app.service.PostCategoryService; import run.halo.app.utils.JsonUtils; /** @@ -18,13 +25,35 @@ * @author guqing * @date 2021-12-04 */ -@ActiveProfiles("test") -@SpringBootTest +@ExtendWith(SpringExtension.class) public class CategoryServiceTest { @Autowired + private ApplicationContext applicationContext; + + @MockBean + private CategoryRepository categoryRepository; + + @MockBean + private PostCategoryService postCategoryService; + + @MockBean + private OptionService optionService; + private CategoryServiceImpl categoryService; + @BeforeEach + public void setUp() { + categoryService = + new CategoryServiceImpl(categoryRepository, postCategoryService, optionService, + applicationContext); + + when(optionService.isEnabledAbsolutePath()).thenReturn(true); + when(optionService.getBlogBaseUrl()).thenReturn("http://127.0.0.1:8090"); + when(optionService.getCategoriesPrefix()).thenReturn("categories"); + when(optionService.getPathSuffix()).thenReturn(""); + } + @Test void listToTree() throws JsonProcessingException { List categories = mockCategories(); From 0fc699cb2cba5a9fb65dab2d9b4491a4b59d9748 Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Tue, 1 Mar 2022 17:18:58 +0800 Subject: [PATCH 2/5] fix: code style --- .../java/run/halo/app/controller/content/model/PostModel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/run/halo/app/controller/content/model/PostModel.java b/src/main/java/run/halo/app/controller/content/model/PostModel.java index 0da00281dc..fd73a38343 100644 --- a/src/main/java/run/halo/app/controller/content/model/PostModel.java +++ b/src/main/java/run/halo/app/controller/content/model/PostModel.java @@ -195,7 +195,8 @@ public String archives(Integer page, Model model) { Page posts = postRenderAssembler.convertToListVo(postPage); - List archives = postRenderAssembler.convertToYearArchives(postPage.getContent()); + List archives = + postRenderAssembler.convertToYearArchives(postPage.getContent()); model.addAttribute("is_archives", true); model.addAttribute("posts", posts); From d226f63b068e5ac272cb08ead57145a266641ed0 Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Wed, 2 Mar 2022 14:23:44 +0800 Subject: [PATCH 3/5] refactor: Transfer post to draft when canceling category encryption --- .../app/listener/post/PostRefreshStatusListener.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/run/halo/app/listener/post/PostRefreshStatusListener.java b/src/main/java/run/halo/app/listener/post/PostRefreshStatusListener.java index 9a2c290198..5e93d7dad5 100644 --- a/src/main/java/run/halo/app/listener/post/PostRefreshStatusListener.java +++ b/src/main/java/run/halo/app/listener/post/PostRefreshStatusListener.java @@ -46,13 +46,13 @@ public void categoryUpdatedListener(CategoryUpdatedEvent event) { return; } boolean isPrivate = categoryService.isPrivate(category.getId()); - if (!isPrivate) { - return; - } + List posts = postCategoryService.listPostBy(category.getId()); - posts.forEach(post -> { - post.setStatus(PostStatus.INTIMATE); - }); + if (isPrivate) { + posts.forEach(post -> post.setStatus(PostStatus.INTIMATE)); + } else { + posts.forEach(post -> post.setStatus(PostStatus.DRAFT)); + } postService.updateInBatch(posts); } From 9c1107980f7e1aea86910d40309d96b85e2d45ad Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Wed, 2 Mar 2022 15:01:39 +0800 Subject: [PATCH 4/5] fix: post authentication --- .../content/auth/PostAuthentication.java | 17 +++++++++++++++-- .../controller/content/model/CategoryModel.java | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java b/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java index 85547fba08..4f4f7d1063 100644 --- a/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java +++ b/src/main/java/run/halo/app/controller/content/auth/PostAuthentication.java @@ -1,10 +1,12 @@ package run.halo.app.controller.content.auth; +import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import run.halo.app.cache.AbstractStringCacheStore; import run.halo.app.model.entity.Post; +import run.halo.app.model.entity.PostCategory; import run.halo.app.model.enums.EncryptTypeEnum; import run.halo.app.service.CategoryService; import run.halo.app.service.PostCategoryService; @@ -23,15 +25,18 @@ public class PostAuthentication implements ContentAuthentication { private final CategoryService categoryService; private final PostCategoryService postCategoryService; private final AbstractStringCacheStore cacheStore; + private final CategoryAuthentication categoryAuthentication; public PostAuthentication(PostService postService, CategoryService categoryService, PostCategoryService postCategoryService, - AbstractStringCacheStore cacheStore) { + AbstractStringCacheStore cacheStore, + CategoryAuthentication categoryAuthentication) { this.postService = postService; this.categoryService = categoryService; this.postCategoryService = postCategoryService; this.cacheStore = cacheStore; + this.categoryAuthentication = categoryAuthentication; } @Override @@ -43,11 +48,19 @@ public Object getPrincipal() { public boolean isAuthenticated(Integer postId) { Post post = postService.getById(postId); if (StringUtils.isBlank(post.getPassword())) { - boolean categoryEncrypted = postCategoryService.listByPostId(postId).stream() + List postCategories = postCategoryService.listByPostId(postId); + boolean categoryEncrypted = postCategories.stream() .anyMatch(postCategory -> categoryService.isPrivate(postCategory.getCategoryId())); if (!categoryEncrypted) { return true; } + + boolean anyCategoryAuthenticated = postCategories.stream() + .anyMatch(postCategory -> + categoryAuthentication.isAuthenticated(postCategory.getCategoryId())); + if (anyCategoryAuthenticated) { + return true; + } } String sessionId = getSessionId(); diff --git a/src/main/java/run/halo/app/controller/content/model/CategoryModel.java b/src/main/java/run/halo/app/controller/content/model/CategoryModel.java index ce996fa7a9..d05bde308d 100644 --- a/src/main/java/run/halo/app/controller/content/model/CategoryModel.java +++ b/src/main/java/run/halo/app/controller/content/model/CategoryModel.java @@ -97,7 +97,7 @@ public String listPost(Model model, String slug, Integer page) { } Set statuses = Sets.immutableEnumSet(PostStatus.PUBLISHED); - if (StringUtils.isNotBlank(category.getPassword())) { + if (categoryService.isPrivate(category.getId())) { statuses = Sets.immutableEnumSet(PostStatus.INTIMATE); } From 716bf9c36421ccac669e479dd85a14b473f96918 Mon Sep 17 00:00:00 2001 From: guqing <1484563614@qq.com> Date: Wed, 2 Mar 2022 15:56:30 +0800 Subject: [PATCH 5/5] refactor: post authentication --- .../content/auth/ContentAuthenticationManager.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/run/halo/app/controller/content/auth/ContentAuthenticationManager.java b/src/main/java/run/halo/app/controller/content/auth/ContentAuthenticationManager.java index 7c6aa67991..8c32d70140 100644 --- a/src/main/java/run/halo/app/controller/content/auth/ContentAuthenticationManager.java +++ b/src/main/java/run/halo/app/controller/content/auth/ContentAuthenticationManager.java @@ -95,6 +95,18 @@ private PostAuthentication authenticatePost(ContentAuthenticationRequest authReq return postAuthentication; } } + + for (Category category : encryptedCategories) { + boolean authenticated = categoryService.lookupFirstEncryptedBy(category.getId()) + .filter(parentCategory -> StringUtils.equals(parentCategory.getPassword(), + authRequest.getPassword())) + .isPresent(); + + if (authenticated) { + postAuthentication.setAuthenticated(post.getId(), true); + return postAuthentication; + } + } throw new AuthenticationException("密码不正确"); } }