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 60858502af..db3b8eb09b 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 @@ -159,9 +159,11 @@ public List updateStatusInBatch(@PathVariable(name = "status") PostStatus public BasePostDetailDTO updateDraftBy( @PathVariable("postId") Integer postId, @RequestBody PostContentParam contentParam) { + Post postToUse = postService.getById(postId); + String formattedContent = contentParam.decideContentBy(postToUse.getEditorType()); // Update draft content - Post post = postService.updateDraftContent(contentParam.getContent(), - contentParam.getContent(), postId); + Post post = postService.updateDraftContent(formattedContent, + contentParam.getOriginalContent(), postId); return postService.convertToDetail(post); } 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 94e32407dc..83a7d43b88 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 @@ -126,9 +126,12 @@ public void updateStatusBy( public BasePostDetailDTO updateDraftBy( @PathVariable("sheetId") Integer sheetId, @RequestBody PostContentParam contentParam) { + Sheet sheetToUse = sheetService.getById(sheetId); + String formattedContent = contentParam.decideContentBy(sheetToUse.getEditorType()); + // Update draft content - Sheet sheet = sheetService.updateDraftContent(contentParam.getContent(), - contentParam.getContent(), sheetId); + Sheet sheet = sheetService.updateDraftContent(formattedContent, + contentParam.getOriginalContent(), sheetId); return sheetService.convertToDetail(sheet); } 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 39e2a4f92f..2d8777c43b 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 @@ -10,7 +10,6 @@ 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.PostEditorType; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.support.HaloConst; import run.halo.app.model.vo.SheetDetailVO; @@ -18,7 +17,6 @@ import run.halo.app.service.SheetMetaService; import run.halo.app.service.SheetService; import run.halo.app.service.ThemeService; -import run.halo.app.utils.MarkdownUtils; /** * Sheet model. @@ -75,12 +73,6 @@ public String content(Sheet sheet, String token, Model model) { } // render markdown to html when preview sheet PatchedContent sheetContent = sheetService.getLatestContentById(sheet.getId()); - if (sheet.getEditorType().equals(PostEditorType.MARKDOWN)) { - sheetContent.setContent( - MarkdownUtils.renderHtml(sheetContent.getOriginalContent())); - } else { - sheetContent.setContent(sheetContent.getOriginalContent()); - } sheet.setContent(sheetContent); } diff --git a/src/main/java/run/halo/app/model/params/BasePostParam.java b/src/main/java/run/halo/app/model/params/BasePostParam.java new file mode 100644 index 0000000000..eab89e6a49 --- /dev/null +++ b/src/main/java/run/halo/app/model/params/BasePostParam.java @@ -0,0 +1,78 @@ +package run.halo.app.model.params; + +import java.util.Date; +import java.util.Objects; +import javax.validation.constraints.Min; +import javax.validation.constraints.Size; +import lombok.Data; +import org.springframework.util.Assert; +import run.halo.app.model.entity.BasePost; +import run.halo.app.model.entity.Content; +import run.halo.app.model.enums.PostEditorType; +import run.halo.app.model.enums.PostStatus; +import run.halo.app.service.impl.BasePostServiceImpl; +import run.halo.app.utils.MarkdownUtils; + +/** + * @author guqing + * @date 2022-02-21 + */ +@Data +public abstract class BasePostParam { + + protected String title; + + protected PostStatus status = PostStatus.DRAFT; + + protected String slug; + + protected String password; + + protected PostEditorType editorType; + + protected String content; + + protected String originalContent; + + protected String summary; + + @Size(max = 1023, message = "封面图链接的字符长度不能超过 {max}") + protected String thumbnail; + + protected Boolean disallowComment = false; + + @Size(max = 255, message = "模版字符长度不能超过 {max}") + protected String template; + + @Min(value = 0, message = "排序字段值不能小于 {value}") + protected Integer topPriority = 0; + + protected Date createTime; + + protected String metaKeywords; + + protected String metaDescription; + + /** + * if {@code true}, it means is that do not let the back-end render the original content + * because the content has been rendered, and you only need to store the original content. + */ + protected Boolean keepRaw = false; + + protected void populateContent(T post) { + Assert.notNull(post, "The post must not be null."); + + Content postContent = new Content(); + postContent.setOriginalContent(originalContent); + + if (Objects.equals(keepRaw, false) + && PostEditorType.MARKDOWN.equals(editorType)) { + postContent.setContent(MarkdownUtils.renderHtml(originalContent)); + } else if (PostEditorType.RICHTEXT.equals(editorType)) { + postContent.setContent(originalContent); + } else { + postContent.setContent(content); + } + post.setContent(Content.PatchedContent.of(postContent)); + } +} diff --git a/src/main/java/run/halo/app/model/params/PostContentParam.java b/src/main/java/run/halo/app/model/params/PostContentParam.java index aac9e3a636..72d4a5512c 100644 --- a/src/main/java/run/halo/app/model/params/PostContentParam.java +++ b/src/main/java/run/halo/app/model/params/PostContentParam.java @@ -1,6 +1,10 @@ package run.halo.app.model.params; +import java.util.Objects; import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import run.halo.app.model.enums.PostEditorType; +import run.halo.app.utils.MarkdownUtils; /** * Post content param. @@ -9,5 +13,35 @@ */ @Data public class PostContentParam { + private String content; + + private String originalContent; + + /** + * if {@code true}, it means is that do not let the back-end render the original content + * because the content has been rendered, and you only need to store the original content. + * otherwise, need server-side rendering. + */ + private Boolean keepRaw = false; + + /** + * Decide on post content based on {@link PostEditorType} and serverSideMarkdownRender. + * + * @param editorType edit type to use + * @return formatted content of post. + */ + public String decideContentBy(PostEditorType editorType) { + String originalContentToUse = StringUtils.defaultString(originalContent, ""); + String result; + if (Objects.equals(keepRaw, false) + && PostEditorType.MARKDOWN.equals(editorType)) { + result = MarkdownUtils.renderHtml(originalContentToUse); + } else if (PostEditorType.RICHTEXT.equals(editorType)) { + result = originalContentToUse; + } else { + result = this.content; + } + return result; + } } diff --git a/src/main/java/run/halo/app/model/params/PostParam.java b/src/main/java/run/halo/app/model/params/PostParam.java index b21a9fa6c5..da9f0e9102 100644 --- a/src/main/java/run/halo/app/model/params/PostParam.java +++ b/src/main/java/run/halo/app/model/params/PostParam.java @@ -1,22 +1,17 @@ package run.halo.app.model.params; -import java.util.Date; import java.util.HashSet; import java.util.Set; -import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.StringUtils; import org.springframework.util.CollectionUtils; import run.halo.app.model.dto.base.InputConverter; -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.enums.PostEditorType; -import run.halo.app.model.enums.PostStatus; -import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.SlugUtils; /** @@ -28,48 +23,46 @@ * @date 2019-03-21 */ @Data -public class PostParam implements InputConverter { +@EqualsAndHashCode(callSuper = true) +public class PostParam extends BasePostParam implements InputConverter { - @NotBlank(message = "文章标题不能为空") - @Size(max = 100, message = "文章标题的字符长度不能超过 {max}") - private String title; - - private PostStatus status = PostStatus.DRAFT; - - @Size(max = 255, message = "文章别名的字符长度不能超过 {max}") - private String slug; - - private PostEditorType editorType; + private Set tagIds; - private String originalContent; + private Set categoryIds; - private String summary; + private Set metas; - @Size(max = 1023, message = "封面图链接的字符长度不能超过 {max}") - private String thumbnail; + @Override + @NotBlank(message = "文章标题不能为空") + @Size(max = 100, message = "文章标题的字符长度不能超过 {max}") + public String getTitle() { + return super.getTitle(); + } - private Boolean disallowComment = false; + @Override + @Size(max = 255, message = "文章别名的字符长度不能超过 {max}") + public String getSlug() { + return super.getSlug(); + } + @Override @Size(max = 255, message = "文章密码的字符长度不能超过 {max}") - private String password; - - @Size(max = 255, message = "Length of template must not be more than {max}") - private String template; - - @Min(value = 0, message = "Post top priority must not be less than {value}") - private Integer topPriority = 0; - - private Date createTime; - - private String metaKeywords; - - private String metaDescription; - - private Set tagIds; + public String getPassword() { + return super.getPassword(); + } - private Set categoryIds; + public Set getPostMetas() { + Set postMetaSet = new HashSet<>(); + if (CollectionUtils.isEmpty(metas)) { + return postMetaSet; + } - private Set metas; + for (PostMetaParam postMetaParam : metas) { + PostMeta postMeta = postMetaParam.convertTo(); + postMetaSet.add(postMeta); + } + return postMetaSet; + } @Override public Post convertTo() { @@ -102,28 +95,4 @@ public void update(Post post) { populateContent(post); InputConverter.super.update(post); } - - public Set getPostMetas() { - Set postMetaSet = new HashSet<>(); - if (CollectionUtils.isEmpty(metas)) { - return postMetaSet; - } - - for (PostMetaParam postMetaParam : metas) { - PostMeta postMeta = postMetaParam.convertTo(); - postMetaSet.add(postMeta); - } - return postMetaSet; - } - - private void populateContent(Post post) { - Content postContent = new Content(); - if (PostEditorType.MARKDOWN.equals(editorType)) { - postContent.setContent(MarkdownUtils.renderHtml(originalContent)); - } else { - postContent.setContent(postContent.getOriginalContent()); - } - postContent.setOriginalContent(originalContent); - post.setContent(PatchedContent.of(postContent)); - } } diff --git a/src/main/java/run/halo/app/model/params/SheetParam.java b/src/main/java/run/halo/app/model/params/SheetParam.java index 65a3c2f21b..d38482020e 100644 --- a/src/main/java/run/halo/app/model/params/SheetParam.java +++ b/src/main/java/run/halo/app/model/params/SheetParam.java @@ -1,22 +1,17 @@ package run.halo.app.model.params; -import java.util.Date; import java.util.HashSet; import java.util.Set; -import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import lombok.Data; +import lombok.EqualsAndHashCode; import org.apache.commons.lang3.StringUtils; import org.springframework.util.CollectionUtils; import run.halo.app.model.dto.base.InputConverter; -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.PostEditorType; -import run.halo.app.model.enums.PostStatus; -import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.SlugUtils; /** @@ -28,44 +23,42 @@ * @date 2019-4-24 */ @Data -public class SheetParam implements InputConverter { +@EqualsAndHashCode(callSuper = true) +public class SheetParam extends BasePostParam implements InputConverter { - @NotBlank(message = "页面标题不能为空") - @Size(max = 100, message = "页面标题的字符长度不能超过 {max}") - private String title; - - private PostStatus status = PostStatus.DRAFT; + private Set metas; + @Override @Size(max = 255, message = "页面别名的字符长度不能超过 {max}") - private String slug; - - private PostEditorType editorType; - - private String originalContent; - - private String summary; - - @Size(max = 255, message = "封面图链接的字符长度不能超过 {max}") - private String thumbnail; + public String getSlug() { + return super.getSlug(); + } - private Boolean disallowComment = false; + @Override + @NotBlank(message = "页面标题不能为空") + @Size(max = 100, message = "页面标题的字符长度不能超过 {max}") + public String getTitle() { + return super.getTitle(); + } + @Override @Size(max = 255, message = "页面密码的字符长度不能超过 {max}") - private String password; - - @Size(max = 255, message = "Length of template must not be more than {max}") - private String template; - - @Min(value = 0, message = "Post top priority must not be less than {value}") - private Integer topPriority = 0; - - private Date createTime; - - private String metaKeywords; + public String getPassword() { + return super.getPassword(); + } - private String metaDescription; + public Set getSheetMetas() { + Set sheetMetasSet = new HashSet<>(); + if (CollectionUtils.isEmpty(metas)) { + return sheetMetasSet; + } - private Set metas; + for (SheetMetaParam sheetMetaParam : metas) { + SheetMeta sheetMeta = sheetMetaParam.convertTo(); + sheetMetasSet.add(sheetMeta); + } + return sheetMetasSet; + } @Override public Sheet convertTo() { @@ -98,28 +91,4 @@ public void update(Sheet sheet) { populateContent(sheet); InputConverter.super.update(sheet); } - - public Set getSheetMetas() { - Set sheetMetasSet = new HashSet<>(); - if (CollectionUtils.isEmpty(metas)) { - return sheetMetasSet; - } - - for (SheetMetaParam sheetMetaParam : metas) { - SheetMeta sheetMeta = sheetMetaParam.convertTo(); - sheetMetasSet.add(sheetMeta); - } - return sheetMetasSet; - } - - private void populateContent(Sheet sheet) { - Content sheetContent = new Content(); - if (PostEditorType.MARKDOWN.equals(editorType)) { - sheetContent.setContent(MarkdownUtils.renderHtml(originalContent)); - } else { - sheetContent.setContent(sheetContent.getOriginalContent()); - } - sheetContent.setOriginalContent(originalContent); - sheet.setContent(PatchedContent.of(sheetContent)); - } } 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 7ec2daf886..b2cd44c5fa 100644 --- a/src/main/java/run/halo/app/service/base/BasePostService.java +++ b/src/main/java/run/halo/app/service/base/BasePostService.java @@ -330,8 +330,8 @@ POST updateDraftContent(@Nullable String content, String originalContent, /** * Generate description. * - * @param content html content must not be null. + * @param content html content. * @return description */ - String generateDescription(@NonNull String content); + String generateDescription(@Nullable String 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 89aeb49a7b..5a57f2d9c0 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -16,6 +16,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -29,7 +30,6 @@ 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.enums.PostEditorType; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.properties.PostProperties; import run.halo.app.repository.base.BasePostRepository; @@ -40,7 +40,6 @@ import run.halo.app.service.base.BasePostService; import run.halo.app.utils.DateUtils; import run.halo.app.utils.HaloUtils; -import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.ServiceUtils; /** @@ -301,23 +300,10 @@ public void increaseLike(Integer postId) { @Transactional public POST createOrUpdateBy(POST post) { Assert.notNull(post, "Post must not be null"); - PostStatus postStatus = post.getStatus(); PatchedContent postContent = post.getContent(); - String originalContent = postContent.getOriginalContent(); - if (originalContent != null) { - // CS304 issue link : https://github.com/halo-dev/halo/issues/1224 - // Render content and set word count - if (post.getEditorType().equals(PostEditorType.MARKDOWN)) { - postContent.setContent(MarkdownUtils.renderHtml(originalContent)); - - post.setWordCount(htmlFormatWordCount(postContent.getContent())); - } else { - postContent.setContent(originalContent); - - post.setWordCount(htmlFormatWordCount(originalContent)); - } - post.setContent(postContent); - } + // word count stat + post.setWordCount(htmlFormatWordCount(postContent.getContent())); + post.setContent(postContent); POST savedPost; // Create or update post @@ -441,14 +427,9 @@ public POST updateDraftContent(String content, String originalContent, Integer p originalContent = ""; } - POST post = getById(postId); - if (PostEditorType.MARKDOWN.equals(post.getEditorType())) { - content = MarkdownUtils.renderHtml(originalContent); - } else { - content = originalContent; - } contentService.createOrUpdateDraftBy(postId, content, originalContent); + POST post = getById(postId); post.setContent(getLatestContentById(postId)); return post; @@ -496,8 +477,10 @@ public List updateStatusByIds(List ids, PostStatus status) { } @Override - public String generateDescription(String content) { - Assert.notNull(content, "html content must not be null"); + public String generateDescription(@Nullable String content) { + if (StringUtils.isBlank(content)) { + return StringUtils.EMPTY; + } String text = HaloUtils.cleanHtmlTag(content); @@ -558,8 +541,10 @@ protected void slugMustNotExist(@NonNull POST post) { } @NonNull - protected String generateSummary(@NonNull String htmlContent) { - Assert.notNull(htmlContent, "html content must not be null"); + protected String generateSummary(@Nullable String htmlContent) { + if (StringUtils.isBlank(htmlContent)) { + return StringUtils.EMPTY; + } String text = HaloUtils.cleanHtmlTag(htmlContent); diff --git a/src/main/java/run/halo/app/utils/PatchUtils.java b/src/main/java/run/halo/app/utils/PatchUtils.java index 1081eb2995..848610921e 100644 --- a/src/main/java/run/halo/app/utils/PatchUtils.java +++ b/src/main/java/run/halo/app/utils/PatchUtils.java @@ -12,8 +12,10 @@ import com.github.difflib.patch.Patch; import com.github.difflib.patch.PatchFailedException; import com.google.common.base.Splitter; +import java.util.Collections; import java.util.List; import lombok.Data; +import org.apache.commons.lang3.StringUtils; /** * Content patch utilities. @@ -76,6 +78,9 @@ public static String diffToJsonPatch(String original, String revised) { } public static List breakLine(String content) { + if (StringUtils.isBlank(content)) { + return Collections.emptyList(); + } return lineSplitter.splitToList(content); } diff --git a/src/test/java/run/halo/app/model/params/PostParamTest.java b/src/test/java/run/halo/app/model/params/PostParamTest.java new file mode 100644 index 0000000000..c89eb8bc67 --- /dev/null +++ b/src/test/java/run/halo/app/model/params/PostParamTest.java @@ -0,0 +1,104 @@ +package run.halo.app.model.params; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import run.halo.app.model.entity.Post; +import run.halo.app.model.enums.PostEditorType; +import run.halo.app.model.enums.PostStatus; + +/** + * Test for {@link PostParam}. + * + * @author guqing + * @date 2022-02-21 + */ +public class PostParamTest { + + private Validator validator; + + @BeforeEach + public void setUp() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + @Test + public void validationTest() { + PostParam postParam = new PostParam(); + postParam.setTitle("Title"); + postParam.setSlug("Slug"); + postParam.setPassword("123"); + postParam.setTopPriority(-1); + + Set> validate = validator.validate(postParam); + assertThat(validate).isNotNull(); + assertThat(validate).hasSize(1); + assertThat(validate.iterator().next().getMessage()).isEqualTo("排序字段值不能小于 0"); + } + + @Test + public void convertToTest() { + PostParam postParam = new PostParam(); + postParam.setTitle("Title"); + postParam.setSlug("Slug"); + postParam.setPassword("123"); + postParam.setStatus(PostStatus.INTIMATE); + postParam.setMetaDescription("Meta description"); + postParam.setTagIds(Set.of(1, 2, 3)); + + Post post = postParam.convertTo(); + assertThat(post).isNotNull(); + assertThat(post.getTitle()).isEqualTo(postParam.getTitle()); + assertThat(post.getSlug()).isEqualTo(postParam.getSlug()); + assertThat(post.getPassword()).isEqualTo(postParam.getPassword()); + } + + @Test + public void shouldServerSideRender() { + PostParam postParam = new PostParam(); + postParam.setSlug("slug"); + // server side rendering + postParam.setKeepRaw(false); + postParam.setOriginalContent("两个黄鹂鸣翠柳,一行白鹭上青天。"); + + Post post = postParam.convertTo(); + assertThat(post).isNotNull(); + assertThat(post.getContent().getContent()).isEqualTo("

两个黄鹂鸣翠柳,一行白鹭上青天。

\n"); + } + + @Test + public void shouldNotServerSideRenderTest() { + PostParam postParam = new PostParam(); + postParam.setSlug("slug"); + // server side render + postParam.setKeepRaw(false); + postParam.setOriginalContent("两个黄鹂鸣翠柳,一行白鹭上青天。"); + + // The keepRaw is true value and edit type is equals to markdown + postParam.setKeepRaw(true); + postParam.setContent("front-end rendering"); + Post post1 = postParam.convertTo(); + assertThat(post1).isNotNull(); + assertThat(post1.getContent().getContent()).isEqualTo(postParam.getContent()); + + // Edit type not equals to markdown and keepRaw is true + postParam.setKeepRaw(true); + postParam.setEditorType(PostEditorType.RICHTEXT); + Post post2 = postParam.convertTo(); + assertThat(post2).isNotNull(); + assertThat(post2.getContent().getContent()).isEqualTo(postParam.getOriginalContent()); + + // Edit type not equals to markdown but want to let server rendering + postParam.setKeepRaw(false); + Post post3 = postParam.convertTo(); + assertThat(post3).isNotNull(); + assertThat(post3.getContent().getContent()).isEqualTo(postParam.getOriginalContent()); + } +}