diff --git a/src/main/java/run/halo/app/config/HaloConfiguration.java b/src/main/java/run/halo/app/config/HaloConfiguration.java index 949593d73..8f646f3e8 100644 --- a/src/main/java/run/halo/app/config/HaloConfiguration.java +++ b/src/main/java/run/halo/app/config/HaloConfiguration.java @@ -121,7 +121,11 @@ public class HaloConfiguration { failureHandler.setObjectMapper(objectMapper); // Config the admin filter - adminAuthenticationFilter.addExcludeUrlPatterns("/api/admin/login"); + adminAuthenticationFilter.addExcludeUrlPatterns( + "/api/admin/login", + "/api/admin/installations", + "/api/admin/recoveries/migration/*" + ); adminAuthenticationFilter.addTryAuthUrlMethodPattern("/api/admin/comments", HttpMethod.POST.name()); adminAuthenticationFilter.addTryAuthUrlMethodPattern("/api/comments", HttpMethod.POST.name()); adminAuthenticationFilter.setFailureHandler( diff --git a/src/main/java/run/halo/app/controller/content/api/InstallController.java b/src/main/java/run/halo/app/controller/admin/api/InstallController.java similarity index 90% rename from src/main/java/run/halo/app/controller/content/api/InstallController.java rename to src/main/java/run/halo/app/controller/admin/api/InstallController.java index c91665dc2..247aa2e86 100644 --- a/src/main/java/run/halo/app/controller/content/api/InstallController.java +++ b/src/main/java/run/halo/app/controller/admin/api/InstallController.java @@ -1,21 +1,23 @@ -package run.halo.app.controller.content.api; +package run.halo.app.controller.admin.api; import freemarker.template.Configuration; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.lang.NonNull; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import run.halo.app.event.logger.LogEvent; import run.halo.app.exception.BadRequestException; import run.halo.app.model.entity.*; import run.halo.app.model.enums.AttachmentType; import run.halo.app.model.enums.LogType; +import run.halo.app.model.params.CategoryParam; import run.halo.app.model.params.InstallParam; import run.halo.app.model.properties.*; import run.halo.app.model.support.BaseResponse; import run.halo.app.service.*; +import run.halo.app.utils.ValidationUtils; import javax.validation.Valid; import java.util.HashMap; @@ -31,7 +33,7 @@ import static run.halo.app.model.support.HaloConst.DEFAULT_THEME_ID; */ @Slf4j @Controller -@RequestMapping("/installations") +@RequestMapping("/api/admin/installations") public class InstallController { private final UserService userService; @@ -70,7 +72,7 @@ public class InstallController { @PostMapping @ResponseBody - public BaseResponse installBlog(@Valid InstallParam installParam) { + public BaseResponse installBlog(@RequestBody @Valid InstallParam installParam) { // TODO Install blog. // Check is installed boolean isInstalled = Boolean.parseBoolean(optionService.getByProperty(PrimaryProperties.IS_INSTALLED).orElse(Boolean.FALSE.toString())); @@ -136,14 +138,17 @@ public class InstallController { return null; } + @NonNull private Category createDefaultCategory() { - Category category = new Category(); + CategoryParam category = new CategoryParam(); - // TODO Multi level category category.setName("未分类"); category.setSlugName("default"); category.setDescription("未分类"); - return categoryService.create(category); + + ValidationUtils.validate(category); + + return categoryService.create(category.convertTo()); } private User createDefaultUser(InstallParam installParam) { diff --git a/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java b/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java index 13d14e8fc..c425d4f41 100644 --- a/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java +++ b/src/main/java/run/halo/app/controller/admin/api/RecoveryController.java @@ -7,6 +7,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import run.halo.app.exception.BadRequestException; +import run.halo.app.model.properties.PrimaryProperties; +import run.halo.app.service.OptionService; import run.halo.app.service.RecoveryService; /** @@ -21,15 +24,23 @@ public class RecoveryController { private final RecoveryService recoveryService; - public RecoveryController(RecoveryService recoveryService) { + private final OptionService optionService; + + public RecoveryController(RecoveryService recoveryService, + OptionService optionService) { this.recoveryService = recoveryService; + this.optionService = optionService; } - @PostMapping - @ApiOperation("Migrate from halo v0.4.3") + @PostMapping("migrations/v0_4_3") + @ApiOperation("Migrates from halo v0.4.3") public void migrateFromVersion_0_4_3( @ApiParam("This file content type should be json") @RequestPart("file") MultipartFile file) { + if (optionService.getByPropertyOrDefault(PrimaryProperties.IS_INSTALLED, Boolean.class, false)) { + throw new BadRequestException("You cannot migrate after blog installing"); + } + recoveryService.migrateFromV0_4_3(file); } } diff --git a/src/main/java/run/halo/app/model/entity/Category.java b/src/main/java/run/halo/app/model/entity/Category.java index fb5319395..f00e85243 100644 --- a/src/main/java/run/halo/app/model/entity/Category.java +++ b/src/main/java/run/halo/app/model/entity/Category.java @@ -5,6 +5,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.hibernate.annotations.SQLDelete; import org.hibernate.annotations.Where; +import org.springframework.core.io.support.ResourcePatternResolver; import javax.persistence.*; @@ -54,6 +55,14 @@ public class Category extends BaseEntity { public void prePersist() { super.prePersist(); id = null; + + if (description == null) { + description = ""; + } + + if (parentId == null || parentId < 0) { + parentId = 0; + } } } diff --git a/src/main/java/run/halo/app/model/params/CategoryParam.java b/src/main/java/run/halo/app/model/params/CategoryParam.java index 0423428c2..cdeef9be1 100644 --- a/src/main/java/run/halo/app/model/params/CategoryParam.java +++ b/src/main/java/run/halo/app/model/params/CategoryParam.java @@ -2,6 +2,7 @@ package run.halo.app.model.params; import run.halo.app.model.dto.base.InputConverter; import run.halo.app.model.entity.Category; +import run.halo.app.utils.HaloUtils; import run.halo.app.utils.SlugUtils; import lombok.Data; import org.apache.commons.lang3.StringUtils; @@ -40,17 +41,17 @@ public class CategoryParam implements InputConverter { /** * Parent category. */ - private Integer parentId; + private Integer parentId = 0; @Override public Category convertTo() { // Handle default value if (StringUtils.isBlank(slugName)) { slugName = SlugUtils.slugify(name); - } - if (parentId == null || parentId < 0) { - parentId = 0; + if (StringUtils.isBlank(slugName)) { + slugName = HaloUtils.initializeUrlIfBlank(slugName); + } } return InputConverter.super.convertTo(); diff --git a/src/main/java/run/halo/app/model/params/InstallParam.java b/src/main/java/run/halo/app/model/params/InstallParam.java index 2cb0ced8f..dc02d3a8c 100644 --- a/src/main/java/run/halo/app/model/params/InstallParam.java +++ b/src/main/java/run/halo/app/model/params/InstallParam.java @@ -19,8 +19,7 @@ public class InstallParam extends UserParam { /** * Blog locale. */ - @NotBlank(message = "Blog locale must not be blank") - private String locale; + private String locale = "zh"; /** * Blog title. diff --git a/src/main/java/run/halo/app/repository/MenuRepository.java b/src/main/java/run/halo/app/repository/MenuRepository.java index a8916394f..8db635230 100644 --- a/src/main/java/run/halo/app/repository/MenuRepository.java +++ b/src/main/java/run/halo/app/repository/MenuRepository.java @@ -5,6 +5,8 @@ import run.halo.app.repository.base.BaseRepository; import org.springframework.lang.NonNull; import run.halo.app.repository.base.BaseRepository; +import java.util.Optional; + /** * Menu repository. * @@ -12,11 +14,7 @@ import run.halo.app.repository.base.BaseRepository; */ public interface MenuRepository extends BaseRepository { - /** - * Exists by menu name. - * - * @param name must not be blank - * @return true if exists; false otherwise - */ boolean existsByName(@NonNull String name); + + boolean existsByIdNotAndName(@NonNull Integer id, @NonNull String name); } diff --git a/src/main/java/run/halo/app/repository/base/BasePostRepository.java b/src/main/java/run/halo/app/repository/base/BasePostRepository.java index 86df97ec1..b4b6994e2 100644 --- a/src/main/java/run/halo/app/repository/base/BasePostRepository.java +++ b/src/main/java/run/halo/app/repository/base/BasePostRepository.java @@ -108,22 +108,9 @@ public interface BasePostRepository extends BaseRepositor */ long countByStatus(@NonNull PostStatus status); - /** - * Count by post url. - * - * @param url post url must not be blank - * @return the count - */ - long countByUrl(@NonNull String url); + boolean countByUrl(@NonNull String title); - /** - * Count by not url and post id not in. - * - * @param id post id must not be null - * @param url post url must not be null - * @return the count - */ - long countByIdNotAndUrl(@NonNull Integer id, @NonNull String url); + boolean countByIdNotAndUrl(@NonNull Integer id, @NonNull String title); /** * Get post by url diff --git a/src/main/java/run/halo/app/service/base/CrudService.java b/src/main/java/run/halo/app/service/base/CrudService.java index 7aa3069c8..91bf70478 100644 --- a/src/main/java/run/halo/app/service/base/CrudService.java +++ b/src/main/java/run/halo/app/service/base/CrudService.java @@ -55,7 +55,7 @@ public interface CrudService { * @return List */ @NonNull - List listAllByIds(@NonNull Collection ids); + List listAllByIds(@Nullable Collection ids); /** * List all by ids and sort @@ -65,7 +65,7 @@ public interface CrudService { * @return List */ @NonNull - List listAllByIds(@NonNull Collection ids, @NonNull Sort sort); + List listAllByIds(@Nullable Collection ids, @NonNull Sort sort); /** * Fetch by id 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 de6d32f4f..17531a35a 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -190,9 +190,6 @@ public abstract class BasePostServiceImpl extends Abstrac public POST createOrUpdateBy(POST post) { Assert.notNull(post, "Post must not be null"); - // Check url - urlMustNotExist(post); - // Render content post.setFormatContent(MarkdownUtils.renderMarkdown(post.getOriginalContent())); @@ -281,6 +278,22 @@ public abstract class BasePostServiceImpl extends Abstrac return new BasePostDetailDTO().convertFrom(post); } + @Override + public POST create(POST post) { + // Check title + urlMustNotExist(post); + + return super.create(post); + } + + @Override + public POST update(POST post) { + // Check title + urlMustNotExist(post); + + return super.update(post); + } + /** * Check if the url is exist. * @@ -288,21 +301,20 @@ public abstract class BasePostServiceImpl extends Abstrac */ protected void urlMustNotExist(@NonNull POST post) { Assert.notNull(post, "Sheet must not be null"); - // TODO Refactor this method with BasePostService - // TODO May refactor these queries // Get url count - long count; + boolean exist; + if (ServiceUtils.isEmptyId(post.getId())) { // The sheet will be created - count = basePostRepository.countByUrl(post.getUrl()); + exist = basePostRepository.countByUrl(post.getUrl()); } else { // The sheet will be updated - count = basePostRepository.countByIdNotAndUrl(post.getId(), post.getUrl()); + exist = basePostRepository.countByIdNotAndUrl(post.getId(), post.getUrl()); } - if (count > 0) { - throw new AlreadyExistsException("The sheet url has been exist"); + if (exist) { + throw new AlreadyExistsException("The post url " + post.getUrl() + " has been exist"); } } } diff --git a/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java b/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java index 559e9aeee..c8574ee6e 100644 --- a/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/MenuServiceImpl.java @@ -13,6 +13,7 @@ import run.halo.app.model.vo.MenuVO; import run.halo.app.repository.MenuRepository; import run.halo.app.service.MenuService; import run.halo.app.service.base.AbstractCrudService; +import run.halo.app.utils.ServiceUtils; import java.util.Collections; import java.util.LinkedList; @@ -46,13 +47,6 @@ public class MenuServiceImpl extends AbstractCrudService implemen public Menu createBy(MenuParam menuParam) { Assert.notNull(menuParam, "Menu param must not be null"); - // Check the name - boolean exists = menuRepository.existsByName(menuParam.getName()); - - if (exists) { - throw new AlreadyExistsException("The menu name " + menuParam.getName() + " has already existed").setErrorData(menuParam.getName()); - } - // Create an return return create(menuParam.convertTo()); } @@ -83,11 +77,25 @@ public class MenuServiceImpl extends AbstractCrudService implemen return topLevelMenu.getChildren(); } + @Override + public Menu create(Menu menu) { + nameMustNotExist(menu); + + return super.create(menu); + } + + @Override + public Menu update(Menu menu) { + nameMustNotExist(menu); + + return super.update(menu); + } + /** * Concrete menu tree. * * @param parentMenu parent menu vo must not be null - * @param menus a list of menu + * @param menus a list of menu */ private void concreteTree(MenuVO parentMenu, List menus) { Assert.notNull(parentMenu, "Parent menu must not be null"); @@ -148,4 +156,22 @@ public class MenuServiceImpl extends AbstractCrudService implemen .map(menu -> new MenuDTO().convertFrom(menu)) .collect(Collectors.toList()); } + + private void nameMustNotExist(@NonNull Menu menu) { + Assert.notNull(menu, "Menu must not be null"); + + boolean exist = false; + + if (ServiceUtils.isEmptyId(menu.getId())) { + // Create action + exist = menuRepository.existsByName(menu.getName()); + } else { + // Update action + exist = menuRepository.existsByIdNotAndName(menu.getId(), menu.getName()); + } + + if (exist) { + throw new AlreadyExistsException("The menu name " + menu.getName() + " already exists"); + } + } } 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 b49711912..64004aac2 100644 --- a/src/main/java/run/halo/app/service/impl/PostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/PostServiceImpl.java @@ -181,7 +181,7 @@ public class PostServiceImpl extends BasePostServiceImpl implements PostSe Assert.notNull(post, "Post param must not be null"); // Create or update post - post = createOrUpdateBy(post); + post = super.createOrUpdateBy(post); // List all tags List tags = tagService.listAllByIds(tagIds); diff --git a/src/main/java/run/halo/app/service/impl/RecoveryServiceImpl.java b/src/main/java/run/halo/app/service/impl/RecoveryServiceImpl.java index 380b0ef9d..f20a932ad 100644 --- a/src/main/java/run/halo/app/service/impl/RecoveryServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/RecoveryServiceImpl.java @@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.util.FileCopyUtils; import org.springframework.web.multipart.MultipartFile; @@ -77,6 +78,7 @@ public class RecoveryServiceImpl implements RecoveryService { } @Override + @Async public void migrateFromV0_4_3(MultipartFile file) { // TODO Async execution // Get migration content @@ -198,7 +200,7 @@ public class RecoveryServiceImpl implements RecoveryService { Post post = BeanUtils.transformFrom(basePost, Post.class); // Create it - Post createdPost = postService.create(post); + Post createdPost = postService.createOrUpdateBy(post); Object commentsObject = postMap.get("comments"); // TODO Handle comments @@ -210,6 +212,7 @@ public class RecoveryServiceImpl implements RecoveryService { try { // Create comments + // TODO Don't use createInBatch method List createdPostComments = postCommentService.createInBatch(postComments); } catch (Exception e) { log.warn("Failed to create post comments for post with id " + createdPost.getId(), e); @@ -224,7 +227,7 @@ public class RecoveryServiceImpl implements RecoveryService { Sheet sheet = BeanUtils.transformFrom(basePost, Sheet.class); // Create it - Sheet createdSheet = sheetService.create(sheet); + Sheet createdSheet = sheetService.createOrUpdateBy(sheet); Object commentsObject = postMap.get("comments"); // TODO Handle comments