From 73b1e606b943627a496264eed1418feb8315cd4b Mon Sep 17 00:00:00 2001 From: ruibaby Date: Thu, 5 Sep 2019 21:29:10 +0800 Subject: [PATCH] Support preview post and sheet. --- .../controller/admin/api/PostController.java | 32 ++++- .../controller/admin/api/SheetController.java | 33 ++++- .../content/ContentPreviewController.java | 116 ++++++++++++++++++ .../run/halo/app/model/entity/BasePost.java | 7 +- .../app/service/impl/BasePostServiceImpl.java | 4 +- 5 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 src/main/java/run/halo/app/controller/content/ContentPreviewController.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 79d1642ef..0413f8464 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 @@ -1,5 +1,6 @@ package run.halo.app.controller.admin.api; +import cn.hutool.core.util.IdUtil; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -8,6 +9,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.web.PageableDefault; import org.springframework.data.web.SortDefault; import org.springframework.web.bind.annotation.*; +import run.halo.app.cache.StringCacheStore; import run.halo.app.model.dto.post.BasePostMinimalDTO; import run.halo.app.model.dto.post.BasePostSimpleDTO; import run.halo.app.model.entity.Post; @@ -16,10 +18,14 @@ import run.halo.app.model.params.PostParam; import run.halo.app.model.params.PostQuery; import run.halo.app.model.vo.PostDetailVO; import run.halo.app.model.vo.PostListVO; +import run.halo.app.service.OptionService; import run.halo.app.service.PostService; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.IOException; import java.util.List; +import java.util.concurrent.TimeUnit; import static org.springframework.data.domain.Sort.Direction.DESC; @@ -36,8 +42,16 @@ public class PostController { private final PostService postService; - public PostController(PostService postService) { + private final StringCacheStore cacheStore; + + private final OptionService optionService; + + public PostController(PostService postService, + StringCacheStore cacheStore, + OptionService optionService) { this.postService = postService; + this.cacheStore = cacheStore; + this.optionService = optionService; } @GetMapping @@ -125,4 +139,20 @@ public class PostController { postService.removeById(postId); } + @GetMapping("preview/{postId:\\d+}") + public void preview(@PathVariable("postId") Integer postId, + HttpServletResponse response) throws IOException { + Post post = postService.getById(postId); + + String token = IdUtil.simpleUUID(); + + // cache preview token + cacheStore.putAny("preview-post-token-" + postId, token, 10, TimeUnit.MINUTES); + + // build preview post url + String url = String.format("%s/preview/post/%s?token=%s", optionService.getBlogBaseUrl(), post.getUrl(), token); + + // redirect to preview url + response.sendRedirect(url); + } } 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 87fdca2e2..5438a7802 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 @@ -1,20 +1,26 @@ package run.halo.app.controller.admin.api; +import cn.hutool.core.util.IdUtil; import io.swagger.annotations.ApiOperation; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.web.bind.annotation.*; +import run.halo.app.cache.StringCacheStore; import run.halo.app.model.dto.InternalSheetDTO; import run.halo.app.model.dto.post.BasePostDetailDTO; import run.halo.app.model.entity.Sheet; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.params.SheetParam; import run.halo.app.model.vo.SheetListVO; +import run.halo.app.service.OptionService; import run.halo.app.service.SheetService; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.IOException; import java.util.List; +import java.util.concurrent.TimeUnit; import static org.springframework.data.domain.Sort.Direction.DESC; @@ -31,8 +37,16 @@ public class SheetController { private final SheetService sheetService; - public SheetController(SheetService sheetService) { + private final StringCacheStore cacheStore; + + private final OptionService optionService; + + public SheetController(SheetService sheetService, + StringCacheStore cacheStore, + OptionService optionService) { this.sheetService = sheetService; + this.cacheStore = cacheStore; + this.optionService = optionService; } @GetMapping("{sheetId:\\d+}") @@ -97,4 +111,21 @@ public class SheetController { Sheet sheet = sheetService.removeById(sheetId); return sheetService.convertToDetail(sheet); } + + @GetMapping("preview/{sheetId:\\d+}") + public void preview(@PathVariable("sheetId") Integer sheetId, + HttpServletResponse response) throws IOException { + Sheet sheet = sheetService.getById(sheetId); + + String token = IdUtil.simpleUUID(); + + // cache preview token + cacheStore.putAny("preview-sheet-token-" + sheetId, token, 10, TimeUnit.MINUTES); + + // build preview sheet url + String url = String.format("%s/preview/s/%s?token=%s", optionService.getBlogBaseUrl(), sheet.getUrl(), token); + + // redirect to preview url + response.sendRedirect(url); + } } diff --git a/src/main/java/run/halo/app/controller/content/ContentPreviewController.java b/src/main/java/run/halo/app/controller/content/ContentPreviewController.java new file mode 100644 index 000000000..7edeba0c8 --- /dev/null +++ b/src/main/java/run/halo/app/controller/content/ContentPreviewController.java @@ -0,0 +1,116 @@ +package run.halo.app.controller.content; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import run.halo.app.cache.StringCacheStore; +import run.halo.app.exception.ForbiddenException; +import run.halo.app.model.entity.Category; +import run.halo.app.model.entity.Post; +import run.halo.app.model.entity.Sheet; +import run.halo.app.model.entity.Tag; +import run.halo.app.model.enums.PostStatus; +import run.halo.app.model.support.HaloConst; +import run.halo.app.service.*; +import run.halo.app.utils.MarkdownUtils; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Post and sheet preview controller. + * + * @author ryanwang + * @date : 2019-09-05 + */ +@Slf4j +@Controller +@RequestMapping(value = "/preview") +public class ContentPreviewController { + + private final PostService postService; + + private final ThemeService themeService; + + private final PostCategoryService postCategoryService; + + private final PostTagService postTagService; + + private final SheetService sheetService; + + private final StringCacheStore cacheStore; + + public ContentPreviewController(PostService postService, + ThemeService themeService, + PostCategoryService postCategoryService, + PostTagService postTagService, + SheetService sheetService, + StringCacheStore cacheStore) { + this.postService = postService; + this.themeService = themeService; + this.postCategoryService = postCategoryService; + this.postTagService = postTagService; + this.sheetService = sheetService; + this.cacheStore = cacheStore; + } + + @GetMapping(value = "post/{url}") + public String post(@PathVariable("url") String url, + @RequestParam(value = "token") String token, + Model model) { + Post post = postService.getBy(PostStatus.DRAFT, url); + + post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); + + // verify token + String cachedToken = cacheStore.getAny("preview-post-token-" + post.getId(), String.class).orElseThrow(() -> new ForbiddenException("该文章的预览链接不存在或已过期")); + + if (!cachedToken.equals(token)) { + throw new ForbiddenException("该文章的预览链接不存在或已过期"); + } + + List categories = postCategoryService.listCategoriesBy(post.getId()); + List tags = postTagService.listTagsBy(post.getId()); + + model.addAttribute("is_post", true); + model.addAttribute("post", post); + model.addAttribute("categories", categories); + model.addAttribute("tags", tags); + + // refresh timeUnit + cacheStore.putAny("preview-post-token-" + post.getId(), token, 10, TimeUnit.MINUTES); + + return themeService.render("post"); + } + + @GetMapping(value = "s/{url}") + public String sheet(@PathVariable("url") String url, + @RequestParam(value = "token") String token, + Model model) { + + Sheet sheet = sheetService.getBy(PostStatus.DRAFT, url); + + sheet.setFormatContent(MarkdownUtils.renderHtml(sheet.getOriginalContent())); + + // verify token + String cachedToken = cacheStore.getAny("preview-sheet-token-" + sheet.getId(), String.class).orElseThrow(() -> new ForbiddenException("该页面的预览链接不存在或已过期")); + + if (!cachedToken.equals(token)) { + throw new ForbiddenException("该页面的预览链接不存在或已过期"); + } + + // sheet and post all can use + model.addAttribute("sheet", sheetService.convertToDetail(sheet)); + model.addAttribute("post", sheetService.convertToDetail(sheet)); + model.addAttribute("is_sheet", true); + + if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) { + return themeService.render(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate()); + } + return themeService.render("sheet"); + } +} diff --git a/src/main/java/run/halo/app/model/entity/BasePost.java b/src/main/java/run/halo/app/model/entity/BasePost.java index 1c5f1ba50..83d3418d7 100644 --- a/src/main/java/run/halo/app/model/entity/BasePost.java +++ b/src/main/java/run/halo/app/model/entity/BasePost.java @@ -10,9 +10,10 @@ import javax.persistence.*; import java.util.Date; /** - * Post entity. + * Post base entity. * * @author johnniang + * @author ryanwang */ @Data @Entity(name = "BasePost") @@ -168,6 +169,10 @@ public class BasePost extends BaseEntity { if (likes == null || likes < 0) { likes = 0L; } + + if (formatContent == null) { + formatContent = ""; + } } } 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 30f2f1a3b..b3389d63d 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -213,7 +213,9 @@ public abstract class BasePostServiceImpl extends Abstrac Assert.notNull(post, "Post must not be null"); // Render content - post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); + if (post.getStatus() == PostStatus.PUBLISHED) { + post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); + } // Create or update post if (ServiceUtils.isEmptyId(post.getId())) {