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 5035bfaa1..9a07b11af 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 @@ -16,7 +16,6 @@ import run.halo.app.security.token.AuthToken; import run.halo.app.service.AdminService; import run.halo.app.service.OptionService; -import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; /** diff --git a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java index 7d0a2ae71..f4025c940 100644 --- a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java +++ b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java @@ -3,7 +3,6 @@ package run.halo.app.controller.content; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.PageUtil; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -14,18 +13,12 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import run.halo.app.cache.StringCacheStore; import run.halo.app.cache.lock.CacheLock; -import run.halo.app.exception.ForbiddenException; -import run.halo.app.model.entity.Category; +import run.halo.app.controller.content.model.PostModel; 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.PostStatus; -import run.halo.app.model.support.HaloConst; import run.halo.app.model.vo.PostListVO; import run.halo.app.service.*; -import run.halo.app.utils.MarkdownUtils; -import java.util.List; import java.util.concurrent.TimeUnit; import static org.springframework.data.domain.Sort.Direction.DESC; @@ -57,13 +50,16 @@ public class ContentArchiveController { private final StringCacheStore cacheStore; + private final PostModel postModel; + public ContentArchiveController(PostService postService, ThemeService themeService, PostCategoryService postCategoryService, PostMetaService postMetaService, PostTagService postTagService, OptionService optionService, - StringCacheStore cacheStore) { + StringCacheStore cacheStore, + PostModel postModel) { this.postService = postService; this.themeService = themeService; this.postCategoryService = postCategoryService; @@ -71,6 +67,7 @@ public class ContentArchiveController { this.postTagService = postTagService; this.optionService = optionService; this.cacheStore = cacheStore; + this.postModel = postModel; } /** @@ -121,43 +118,7 @@ public class ContentArchiveController { Model model) { Post post = postService.getByUrl(url); - if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) { - String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl()); - return "redirect:" + redirect; - } - - if (StringUtils.isEmpty(token)) { - post = postService.getBy(PostStatus.PUBLISHED, url); - } else { - // verify token - String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限")); - if (!cachedToken.equals(token)) { - throw new ForbiddenException("您没有该文章的访问权限"); - } - post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); - } - postService.publishVisitEvent(post.getId()); - postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost)); - postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost)); - - List categories = postCategoryService.listCategoriesBy(post.getId()); - List tags = postTagService.listTagsBy(post.getId()); - List metas = postMetaService.listBy(post.getId()); - - model.addAttribute("is_post", true); - model.addAttribute("post", postService.convertToDetailVo(post)); - model.addAttribute("categories", categories); - model.addAttribute("tags", tags); - model.addAttribute("metas", postMetaService.convertToMap(metas)); - - // TODO,Will be deprecated - model.addAttribute("comments", Page.empty()); - - if (themeService.templateExists(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate() + HaloConst.SUFFIX_FTL)) { - return themeService.render(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate()); - } - - return themeService.render("post"); + return postModel.post(post, token, model); } @GetMapping(value = "{url}/password") diff --git a/src/main/java/run/halo/app/controller/content/ContentIndexController.java b/src/main/java/run/halo/app/controller/content/ContentIndexController.java index 476184d98..46f4d4bbe 100644 --- a/src/main/java/run/halo/app/controller/content/ContentIndexController.java +++ b/src/main/java/run/halo/app/controller/content/ContentIndexController.java @@ -11,7 +11,9 @@ 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 run.halo.app.controller.content.model.PostModel; import run.halo.app.model.entity.Post; +import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.properties.PostProperties; import run.halo.app.model.vo.PostListVO; @@ -19,6 +21,8 @@ import run.halo.app.service.OptionService; import run.halo.app.service.PostService; import run.halo.app.service.ThemeService; +import java.util.Objects; + import static org.springframework.data.domain.Sort.Direction.DESC; /** @@ -38,23 +42,36 @@ public class ContentIndexController { private final ThemeService themeService; + private final PostModel postModel; + public ContentIndexController(PostService postService, OptionService optionService, - ThemeService themeService) { + ThemeService themeService, + PostModel postModel) { this.postService = postService; this.optionService = optionService; this.themeService = themeService; + this.postModel = postModel; } /** * Render blog index * + * @param p post id * @param model model * @return template path: themes/{theme}/index.ftl */ @GetMapping - public String index(Model model) { + public String index(Integer p, String token, Model model) { + + PostPermalinkType permalinkType = optionService.getPostPermalinkType(); + + if (PostPermalinkType.ID.equals(permalinkType) && !Objects.isNull(p)) { + Post post = postService.getById(p); + return postModel.post(post, token, model); + } + return this.index(model, 1); } 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 new file mode 100644 index 000000000..be73cabe7 --- /dev/null +++ b/src/main/java/run/halo/app/controller/content/model/PostModel.java @@ -0,0 +1,95 @@ +package run.halo.app.controller.content.model; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Component; +import org.springframework.ui.Model; +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.PostMeta; +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; + +/** + * @author ryan0up + * @date 2020/1/7 + */ +@Component +public class PostModel { + + private final PostService postService; + + private final ThemeService themeService; + + private final PostCategoryService postCategoryService; + + private final PostMetaService postMetaService; + + private final PostTagService postTagService; + + private final OptionService optionService; + + private final StringCacheStore cacheStore; + + public PostModel(PostService postService, + ThemeService themeService, + PostCategoryService postCategoryService, + PostMetaService postMetaService, + PostTagService postTagService, + OptionService optionService, + StringCacheStore cacheStore) { + this.postService = postService; + this.themeService = themeService; + this.postCategoryService = postCategoryService; + this.postMetaService = postMetaService; + this.postTagService = postTagService; + this.optionService = optionService; + this.cacheStore = cacheStore; + } + + public String post(Post post, String token, Model model) { + + if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) { + String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl()); + return "redirect:" + redirect; + } + + if (!StringUtils.isEmpty(token)) { + // verify token + String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限")); + if (!cachedToken.equals(token)) { + throw new ForbiddenException("您没有该文章的访问权限"); + } + post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); + } + postService.publishVisitEvent(post.getId()); + postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost)); + postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost)); + + List categories = postCategoryService.listCategoriesBy(post.getId()); + List tags = postTagService.listTagsBy(post.getId()); + List metas = postMetaService.listBy(post.getId()); + + model.addAttribute("is_post", true); + model.addAttribute("post", postService.convertToDetailVo(post)); + model.addAttribute("categories", categories); + model.addAttribute("tags", tags); + model.addAttribute("metas", postMetaService.convertToMap(metas)); + + // TODO,Will be deprecated + model.addAttribute("comments", Page.empty()); + + if (themeService.templateExists(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate() + HaloConst.SUFFIX_FTL)) { + return themeService.render(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate()); + } + + return themeService.render("post"); + } +} diff --git a/src/main/java/run/halo/app/model/enums/PostPermalinkType.java b/src/main/java/run/halo/app/model/enums/PostPermalinkType.java new file mode 100644 index 000000000..e0ae85854 --- /dev/null +++ b/src/main/java/run/halo/app/model/enums/PostPermalinkType.java @@ -0,0 +1,42 @@ +package run.halo.app.model.enums; + +/** + * Post Permalink type enum. + * + * @author ryanwang + * @date 2020-01-07 + */ +public enum PostPermalinkType implements ValueEnum { + + /** + * /archives/${url} + */ + DEFAULT(0), + + /** + * /1970/01/01/${url} + */ + DATE(1), + + /** + * /1970/01/${url} + */ + DAY(2), + + /** + * /?p=${id} + */ + ID(3); + + private Integer value; + + PostPermalinkType(Integer value) { + this.value = value; + } + + + @Override + public Integer getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/properties/PermalinkProperties.java b/src/main/java/run/halo/app/model/properties/PermalinkProperties.java new file mode 100644 index 000000000..e5307936e --- /dev/null +++ b/src/main/java/run/halo/app/model/properties/PermalinkProperties.java @@ -0,0 +1,68 @@ +package run.halo.app.model.properties; + +import run.halo.app.model.enums.PostPermalinkType; + +/** + * Permalink properties enum. + * + * @author ryanwang + * @date 2020-01-07 + */ +public enum PermalinkProperties implements PropertyEnum { + + /** + * Post Permalink type. + */ + POST_PERMALINK_TYPE("post_permalink_type", PostPermalinkType.class, PostPermalinkType.DEFAULT.name()), + + /** + * Categories prefix + * such as: /categories or /categories/${slugName} + */ + CATEGORIES_PREFIX("categories_prefix", String.class, "categories"), + + /** + * Tags prefix + * such as: /tags or /tags/${slugName} + */ + TAGS_PREFIX("tags_prefix", String.class, "tags"), + + /** + * Archives prefix. + * such as: /archives + */ + ARCHIVES_PREFIX("archives_prefix", String.class, "archives"), + + /** + * Sheet prefix + * such as: /s/${url} + */ + SHEET_PREFIX("sheet_prefix", String.class, "s"); + + private final String value; + + private final Class type; + + private final String defaultValue; + + PermalinkProperties(String value, Class type, String defaultValue) { + this.value = value; + this.type = type; + this.defaultValue = defaultValue; + } + + @Override + public Class getType() { + return type; + } + + @Override + public String defaultValue() { + return defaultValue; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/properties/PropertyEnum.java b/src/main/java/run/halo/app/model/properties/PropertyEnum.java index 32e680ff1..240053224 100644 --- a/src/main/java/run/halo/app/model/properties/PropertyEnum.java +++ b/src/main/java/run/halo/app/model/properties/PropertyEnum.java @@ -158,6 +158,7 @@ public interface PropertyEnum extends ValueEnum { propertyEnumClasses.add(StaticDeployProperties.class); propertyEnumClasses.add(GitStaticDeployProperties.class); propertyEnumClasses.add(NetlifyStaticDeployProperties.class); + propertyEnumClasses.add(PermalinkProperties.class); Map result = new HashMap<>(); diff --git a/src/main/java/run/halo/app/model/vo/PostListVO.java b/src/main/java/run/halo/app/model/vo/PostListVO.java index 80db07fb0..aba7be306 100644 --- a/src/main/java/run/halo/app/model/vo/PostListVO.java +++ b/src/main/java/run/halo/app/model/vo/PostListVO.java @@ -14,12 +14,15 @@ import java.util.List; * * @author johnniang * @author guqing + * @author ryanwang * @date 2019-03-19 */ @EqualsAndHashCode(callSuper = true) @Data public class PostListVO extends BasePostSimpleDTO { + private String fullPath; + private Long commentCount; private List tags; 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 733034e39..396060ee8 100644 --- a/src/main/java/run/halo/app/repository/base/BasePostRepository.java +++ b/src/main/java/run/halo/app/repository/base/BasePostRepository.java @@ -18,6 +18,7 @@ import java.util.Optional; * Base post repository. * * @author johnniang + * @author ryanwang * @date 2019-03-22 */ public interface BasePostRepository extends BaseRepository { @@ -99,6 +100,16 @@ public interface BasePostRepository extends BaseRepositor @NonNull Optional getByUrlAndStatus(@NonNull String url, @NonNull PostStatus status); + /** + * Gets post by id and status. + * + * @param id id must not be blank + * @param status status must not be null + * @return an optional post + */ + @NonNull + Optional getByIdAndStatus(@NonNull Integer id, @NonNull PostStatus status); + /** * Counts posts by status and type. diff --git a/src/main/java/run/halo/app/service/AdminService.java b/src/main/java/run/halo/app/service/AdminService.java index d3533af26..2a3426664 100644 --- a/src/main/java/run/halo/app/service/AdminService.java +++ b/src/main/java/run/halo/app/service/AdminService.java @@ -7,8 +7,6 @@ import run.halo.app.model.params.LoginParam; import run.halo.app.model.params.ResetPasswordParam; import run.halo.app.security.token.AuthToken; -import javax.servlet.http.HttpServletResponse; - /** * Admin service interface. * diff --git a/src/main/java/run/halo/app/service/OptionService.java b/src/main/java/run/halo/app/service/OptionService.java index ef29ca479..450cc924d 100755 --- a/src/main/java/run/halo/app/service/OptionService.java +++ b/src/main/java/run/halo/app/service/OptionService.java @@ -10,6 +10,7 @@ import run.halo.app.exception.MissingPropertyException; import run.halo.app.model.dto.OptionDTO; import run.halo.app.model.dto.OptionSimpleDTO; import run.halo.app.model.entity.Option; +import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.ValueEnum; import run.halo.app.model.params.OptionParam; import run.halo.app.model.params.OptionQuery; @@ -338,6 +339,13 @@ public interface OptionService extends CrudService { */ long getBirthday(); + /** + * Get post permalink type. + * + * @return PostPermalinkType + */ + PostPermalinkType getPostPermalinkType(); + /** * Replace option url in batch. * 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 45610a7ff..3d48b209a 100644 --- a/src/main/java/run/halo/app/service/base/BasePostService.java +++ b/src/main/java/run/halo/app/service/base/BasePostService.java @@ -18,7 +18,8 @@ import java.util.Optional; * Base post service implementation. * * @author johnniang - * @date 19-4-24 + * @author ryanwang + * @date 2019-04-24 */ public interface BasePostService extends CrudService { @@ -63,6 +64,16 @@ public interface BasePostService extends CrudService extends Abstrac return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url)); } + @Override + public POST getBy(PostStatus status, Integer id) { + Assert.notNull(status, "Post status must not be null"); + Assert.notNull(id, "Post id must not be null"); + + Optional postOptional = basePostRepository.getByIdAndStatus(id, status); + + return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(id)); + } + @Override public List listAllBy(PostStatus status) { Assert.notNull(status, "Post status must not be null"); diff --git a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java index a0e7ffb3f..ef3fefaef 100644 --- a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java @@ -22,6 +22,7 @@ import run.halo.app.exception.MissingPropertyException; import run.halo.app.model.dto.OptionDTO; import run.halo.app.model.dto.OptionSimpleDTO; import run.halo.app.model.entity.Option; +import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.ValueEnum; import run.halo.app.model.params.OptionParam; import run.halo.app.model.params.OptionQuery; @@ -461,6 +462,11 @@ public class OptionServiceImpl extends AbstractCrudService impl }); } + @Override + public PostPermalinkType getPostPermalinkType() { + return getEnumByPropertyOrDefault(PermalinkProperties.POST_PERMALINK_TYPE, PostPermalinkType.class, PostPermalinkType.DEFAULT); + } + @Override public List replaceUrl(String oldUrl, String newUrl) { List