diff --git a/src/main/java/run/halo/app/controller/admin/api/AdminController.java b/src/main/java/run/halo/app/controller/admin/api/AdminController.java index 2dadc1e94..d795d795d 100644 --- a/src/main/java/run/halo/app/controller/admin/api/AdminController.java +++ b/src/main/java/run/halo/app/controller/admin/api/AdminController.java @@ -113,8 +113,10 @@ public class AdminController { adminService.updateApplicationConfig(content); } - @PostMapping("/spring/restart") + @PostMapping(value = {"halo/restart", "spring/restart"}) + @ApiOperation("Restart halo server") public void restartApplication() { Application.restart(); } + } diff --git a/src/main/java/run/halo/app/controller/content/MainController.java b/src/main/java/run/halo/app/controller/content/MainController.java index ac1532b60..3c6171b20 100644 --- a/src/main/java/run/halo/app/controller/content/MainController.java +++ b/src/main/java/run/halo/app/controller/content/MainController.java @@ -88,5 +88,4 @@ public class MainController { response.sendRedirect(favicon); } } - } diff --git a/src/main/java/run/halo/app/controller/core/CommonController.java b/src/main/java/run/halo/app/controller/core/CommonController.java index f86deeedb..739096875 100644 --- a/src/main/java/run/halo/app/controller/core/CommonController.java +++ b/src/main/java/run/halo/app/controller/core/CommonController.java @@ -67,8 +67,6 @@ public class CommonController extends AbstractErrorController { */ @GetMapping public String handleError(HttpServletRequest request, HttpServletResponse response, Model model) { - HttpStatus status = getStatus(request); - log.error("Request URL: [{}], URI: [{}], Request Method: [{}], IP: [{}]", request.getRequestURL(), request.getRequestURI(), @@ -82,6 +80,8 @@ public class CommonController extends AbstractErrorController { log.debug("Error detail: [{}]", errorDetail); + HttpStatus status = getStatus(request); + if (status.equals(HttpStatus.INTERNAL_SERVER_ERROR)) { return contentInternalError(); } else if (status.equals(HttpStatus.NOT_FOUND)) { diff --git a/src/main/java/run/halo/app/event/post/PostVisitEvent.java b/src/main/java/run/halo/app/event/post/PostVisitEvent.java index f7d0d8b1c..ceb41d614 100644 --- a/src/main/java/run/halo/app/event/post/PostVisitEvent.java +++ b/src/main/java/run/halo/app/event/post/PostVisitEvent.java @@ -1,5 +1,9 @@ package run.halo.app.event.post; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import run.halo.app.utils.ServiceUtils; + /** * Post visit event. * @@ -14,7 +18,8 @@ public class PostVisitEvent extends AbstractVisitEvent { * @param source the object on which the event initially occurred (never {@code null}) * @param postId post id must not be null */ - public PostVisitEvent(Object source, Integer postId) { + public PostVisitEvent(Object source, @NonNull Integer postId) { super(source, postId); + Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty"); } } diff --git a/src/main/java/run/halo/app/listener/StartedListener.java b/src/main/java/run/halo/app/listener/StartedListener.java index 130923e43..c8cda9b82 100644 --- a/src/main/java/run/halo/app/listener/StartedListener.java +++ b/src/main/java/run/halo/app/listener/StartedListener.java @@ -7,6 +7,8 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; import org.springframework.util.ResourceUtils; import run.halo.app.config.properties.HaloProperties; import run.halo.app.model.properties.PrimaryProperties; @@ -15,6 +17,7 @@ import run.halo.app.service.ThemeService; import run.halo.app.service.UserService; import run.halo.app.utils.FileUtils; +import java.io.IOException; import java.net.URI; import java.nio.file.*; import java.util.Collections; @@ -76,8 +79,9 @@ public class StartedListener implements ApplicationListener extends BaseRepositor @Query("update BasePost p set p.likes = p.likes + :likes where p.id = :postId") int updateLikes(@Param("likes") long likes, @Param("postId") @NonNull Integer postId); + /** + * Updates post original content/ + * + * @param content content could be blank but disallow to be null + * @param postId post id must not be null + * @return updated rows + */ + @Modifying + @Query("update BasePost p set p.originalContent = :content where p.id = :postId") + int updateOriginalContent(@Param("content") @NonNull String content, @Param("postId") @NonNull Integer postId); + + @Modifying + @Query("update BasePost p set p.status = :status where p.id = :postId") + int updateStatus(@Param("status") @NonNull PostStatus status, @Param("postId") @NonNull Integer postId); + + @Modifying + @Query("update BasePost p set p.formatContent = :formatContent where p.id = :postId") + int updateFormatContent(@Param("formatContent") @NonNull String formatContent, @Param("postId") @NonNull Integer postId); } 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 520e045a5..24f00e263 100644 --- a/src/main/java/run/halo/app/service/base/BasePostService.java +++ b/src/main/java/run/halo/app/service/base/BasePostService.java @@ -8,6 +8,7 @@ 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.Post; import run.halo.app.model.enums.PostStatus; import java.util.Date; @@ -215,4 +216,24 @@ public interface BasePostService extends CrudService extends Abstrac @Override public Map convertToMap(List metas) { - if (CollectionUtils.isEmpty(metas)) { - return Collections.emptyMap(); - } - - Map result = new HashMap<>(); - metas.forEach(meta -> result.put(meta.getKey(), meta.getValue())); - - return result; + return ServiceUtils.convertToMap(metas, META::getKey, META::getValue); } 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 f6a71aeac..e083194c3 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.util.CollectionUtils; import run.halo.app.exception.AlreadyExistsException; 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; @@ -320,6 +321,65 @@ public abstract class BasePostServiceImpl extends Abstrac return new BasePostDetailDTO().convertFrom(post); } + @Override + @Transactional + public POST updateDraftContent(String content, Integer postId) { + Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty"); + + if (content == null) { + content = ""; + } + + POST post = getById(postId); + + if (!StringUtils.equals(content, post.getOriginalContent())) { + // If content is different with database, then update database + int updatedRows = basePostRepository.updateOriginalContent(content, postId); + if (updatedRows != 1) { + throw new ServiceException("Failed to update original content of post with id " + postId); + } + // Set the content + post.setOriginalContent(content); + } + + return post; + } + + @Override + @Transactional + public POST updateStatus(PostStatus status, Integer postId) { + Assert.notNull(status, "Post status must not be null"); + Assert.isTrue(!ServiceUtils.isEmptyId(postId), "Post id must not be empty"); + + // Get post + POST post = getById(postId); + + if (!status.equals(post.getStatus())) { + // Update post + int updatedRows = basePostRepository.updateStatus(status, postId); + if (updatedRows != 1) { + throw new ServiceException("Failed to update post status of post with id " + postId); + } + + post.setStatus(status); + } + + // Sync content + if (PostStatus.PUBLISHED.equals(status)) { + // If publish this post, then convert the formatted content + String formatContent = MarkdownUtils.renderHtml(post.getOriginalContent()); + int updatedRows = basePostRepository.updateFormatContent(formatContent, postId); + + if (updatedRows != 1) { + throw new ServiceException("Failed to update post format content of post with id " + postId); + } + + post.setFormatContent(formatContent); + } + + return post; + } + @Override public POST create(POST post) { // Check title diff --git a/src/main/java/run/halo/app/utils/MarkdownUtils.java b/src/main/java/run/halo/app/utils/MarkdownUtils.java index f931712bd..e0ca62b82 100644 --- a/src/main/java/run/halo/app/utils/MarkdownUtils.java +++ b/src/main/java/run/halo/app/utils/MarkdownUtils.java @@ -67,7 +67,7 @@ public class MarkdownUtils { /** * Render Markdown content * - * @param content content + * @param markdown content * @return String */ public static String renderHtml(String markdown) { @@ -108,7 +108,7 @@ public class MarkdownUtils { /** * Get front-matter * - * @param content content + * @param markdown markdown * @return Map */ public static Map> getFrontMatter(String markdown) { diff --git a/src/main/java/run/halo/app/utils/ServiceUtils.java b/src/main/java/run/halo/app/utils/ServiceUtils.java index ccb4480a9..bc04025cc 100644 --- a/src/main/java/run/halo/app/utils/ServiceUtils.java +++ b/src/main/java/run/halo/app/utils/ServiceUtils.java @@ -99,8 +99,9 @@ public class ServiceUtils { * @return a map which key from list data and value is data */ @NonNull - public static Map convertToMap(Collection list, Function keyFunction, Function valueFunction) { - Assert.notNull(keyFunction, "mapping function must not be null"); + public static Map convertToMap(@Nullable Collection list, @NonNull Function keyFunction, @NonNull Function valueFunction) { + Assert.notNull(keyFunction, "Key function must not be null"); + Assert.notNull(valueFunction, "Value function must not be null"); if (CollectionUtils.isEmpty(list)) { return Collections.emptyMap(); diff --git a/src/main/resources/application-test.yaml b/src/main/resources/application-test.yaml index ab4e4c3fe..fc273f032 100755 --- a/src/main/resources/application-test.yaml +++ b/src/main/resources/application-test.yaml @@ -14,10 +14,10 @@ spring: type: com.zaxxer.hikari.HikariDataSource # H2 Database 配置 - driver-class-name: org.h2.Driver - url: jdbc:h2:file:~/halo-test/db/halo - username: admin - password: 123456 +# driver-class-name: org.h2.Driver +# url: jdbc:h2:file:~/halo-test/db/halo +# username: admin +# password: 123456 # MySQL 配置 # driver-class-name: com.mysql.cj.jdbc.Driver diff --git a/src/test/java/run/halo/app/service/impl/PostServiceImplTest.java b/src/test/java/run/halo/app/service/impl/PostServiceImplTest.java index 23613eca5..fe62df791 100644 --- a/src/test/java/run/halo/app/service/impl/PostServiceImplTest.java +++ b/src/test/java/run/halo/app/service/impl/PostServiceImplTest.java @@ -7,13 +7,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.annotation.Transactional; @RunWith(SpringRunner.class) @SpringBootTest -@ActiveProfiles("dev") +@ActiveProfiles("test") public class PostServiceImplTest { - String standardMdContent = "---\n" + + private String standardMdContent = "---\n" + "title: springfox-swagger2配置成功但无法访问/swagger-ui.html\n" + "tags:\n" + " - spring boot\n" + @@ -26,7 +27,7 @@ public class PostServiceImplTest { "\n" + "在前后端分离项目中,通常需要用到 API 文档,springfox 开发的 **[SpringFox](https://github.com/springfox/springfox)** 可以实现自动化 json API 文档。"; - String nonStandardMdContent = "---\n" + + private String nonStandardMdContent = "---\n" + "title: Basic concepts of JPA\n" + "date: 2018-08-03 11:57:00\n" + "tags: ['spring', 'jpa', 'database', 'concept']\n" + @@ -45,6 +46,7 @@ public class PostServiceImplTest { } @Test + @Transactional public void markdownImportTest() { postService.importMarkdown(standardMdContent, "standard"); postService.importMarkdown(nonStandardMdContent, "nonStandard");