* Update FUNDING.yml

* merge from master

* feat: update page default sort && update the pre and next of posts

* feat: adapter dev branch

* Feat: update getAdjacentPosts return && update post ftl

* fix: revert gradle-wrapper.jar

Co-authored-by: Ryan Wang <i@ryanc.cc>
Co-authored-by: Jiahuan Shen <shenjiahuan1999@hotmail.com>
Co-authored-by: John Niang <johnniang@riseup.net>
Co-authored-by: weiwensangsang <weiwensangsang@users.noreply.github.com>
Co-authored-by: Lei XinXin <40338580+NGLSL@users.noreply.github.com>
Co-authored-by: 寒山 <ms915818993@163.com>
Co-authored-by: IJKZEN <ijkzen@outlook.com>
pull/503/head
leozhou 2020-01-16 18:00:33 +08:00 committed by Ryan Wang
parent f4c2ca2f8d
commit 89086dd345
9 changed files with 264 additions and 76 deletions

2
.github/FUNDING.yml vendored
View File

@ -5,4 +5,4 @@ patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: https://pay.ryanc.cc custom: ['https://pay.ryanc.cc']

View File

@ -1,6 +1,8 @@
package run.halo.app.controller.content; package run.halo.app.controller.content;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;

View File

@ -64,7 +64,7 @@ public class ContentIndexController {
* Render blog index * Render blog index
* *
* @param model model * @param model model
* @param page current page number * @param page current page number
* @return template path: themes/{theme}/index.ftl * @return template path: themes/{theme}/index.ftl
*/ */
@GetMapping(value = "page/{page}") @GetMapping(value = "page/{page}")

View File

@ -1,11 +1,11 @@
package run.halo.app.controller.content.model; package run.halo.app.controller.content.model;
import cn.hutool.core.util.PageUtil; import cn.hutool.core.util.PageUtil;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import run.halo.app.cache.StringCacheStore; import run.halo.app.cache.StringCacheStore;
@ -15,16 +15,14 @@ import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostMeta; import run.halo.app.model.entity.PostMeta;
import run.halo.app.model.entity.Tag; import run.halo.app.model.entity.Tag;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.properties.PostProperties;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
import run.halo.app.model.vo.AdjacentPostVO;
import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostListVO;
import run.halo.app.service.*; import run.halo.app.service.*;
import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.MarkdownUtils;
import java.util.List; import java.util.List;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* Post Model * Post Model
* *
@ -53,14 +51,14 @@ public class PostModel {
private final StringCacheStore cacheStore; private final StringCacheStore cacheStore;
public PostModel(PostService postService, public PostModel(PostService postService,
ThemeService themeService, ThemeService themeService,
PostCategoryService postCategoryService, PostCategoryService postCategoryService,
CategoryService categoryService, CategoryService categoryService,
PostMetaService postMetaService, PostMetaService postMetaService,
PostTagService postTagService, PostTagService postTagService,
TagService tagService, TagService tagService,
OptionService optionService, OptionService optionService,
StringCacheStore cacheStore) { StringCacheStore cacheStore) {
this.postService = postService; this.postService = postService;
this.themeService = themeService; this.themeService = themeService;
this.postCategoryService = postCategoryService; this.postCategoryService = postCategoryService;
@ -75,21 +73,26 @@ public class PostModel {
public String content(Post post, String token, Model model) { public String content(Post post, String token, Model model) {
if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) { if (post.getStatus().equals(PostStatus.INTIMATE) && StringUtils.isEmpty(token)) {
String redirect = String.format("%s/archives/%s/password", optionService.getBlogBaseUrl(), post.getUrl()); String redirect = String
.format("%s/archives/%s/password", optionService.getBlogBaseUrl(),
post.getUrl());
return "redirect:" + redirect; return "redirect:" + redirect;
} }
if (!StringUtils.isEmpty(token)) { if (!StringUtils.isEmpty(token)) {
// verify token // verify token
String cachedToken = cacheStore.getAny(token, String.class).orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限")); String cachedToken = cacheStore.getAny(token, String.class)
.orElseThrow(() -> new ForbiddenException("您没有该文章的访问权限"));
if (!cachedToken.equals(token)) { if (!cachedToken.equals(token)) {
throw new ForbiddenException("您没有该文章的访问权限"); throw new ForbiddenException("您没有该文章的访问权限");
} }
post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent())); post.setFormatContent(MarkdownUtils.renderHtml(post.getOriginalContent()));
} }
postService.publishVisitEvent(post.getId()); postService.publishVisitEvent(post.getId());
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost)); AdjacentPostVO adjacentPostVO = postService.getAdjacentPosts(post);
adjacentPostVO.getOptionalPrePost().ifPresent(prePost -> model.addAttribute("prePost", prePost));
adjacentPostVO.getOptionalNextPost().ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
List<Category> categories = postCategoryService.listCategoriesBy(post.getId()); List<Category> categories = postCategoryService.listCategoriesBy(post.getId());
List<Tag> tags = postTagService.listTagsBy(post.getId()); List<Tag> tags = postTagService.listTagsBy(post.getId());
@ -104,7 +107,8 @@ public class PostModel {
// TODO,Will be deprecated // TODO,Will be deprecated
model.addAttribute("comments", Page.empty()); model.addAttribute("comments", Page.empty());
if (themeService.templateExists(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate() + HaloConst.SUFFIX_FTL)) { if (themeService.templateExists(
ThemeService.CUSTOM_POST_PREFIX + post.getTemplate() + HaloConst.SUFFIX_FTL)) {
return themeService.render(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate()); return themeService.render(ThemeService.CUSTOM_POST_PREFIX + post.getTemplate());
} }
@ -112,9 +116,9 @@ public class PostModel {
} }
public String list(Integer page, Model model, String decide, String template) { public String list(Integer page, Model model, String decide, String template) {
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
int pageSize = optionService.getPostPageSize(); int pageSize = optionService.getPostPageSize();
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort))); Pageable pageable = PageRequest
.of(page >= 1 ? page - 1 : page, pageSize, postService.getPostDefaultSort());
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
Page<PostListVO> posts = postService.convertToListVo(postPage); Page<PostListVO> posts = postService.convertToListVo(postPage);

View File

@ -0,0 +1,33 @@
package run.halo.app.model.vo;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import run.halo.app.model.entity.Post;
/**
* AdjacentPost class
*
* @author zhouchunjie
* @date 2020/1/12
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AdjacentPostVO {
private Post prePost;
private Post nextPost;
public Optional<Post> getOptionalPrePost(){
return Optional.ofNullable(this.getPrePost());
}
public Optional<Post> getOptionalNextPost() {
return Optional.ofNullable(this.getNextPost());
}
}

View File

@ -1,5 +1,8 @@
package run.halo.app.repository.base; package run.halo.app.repository.base;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@ -10,10 +13,6 @@ import org.springframework.lang.NonNull;
import run.halo.app.model.entity.BasePost; import run.halo.app.model.entity.BasePost;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/** /**
* Base post repository. * Base post repository.
* *

View File

@ -1,12 +1,15 @@
package run.halo.app.service; package run.halo.app.service;
import javax.validation.constraints.NotNull;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostMeta; import run.halo.app.model.entity.PostMeta;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.PostQuery; import run.halo.app.model.params.PostQuery;
import run.halo.app.model.vo.AdjacentPostVO;
import run.halo.app.model.vo.ArchiveMonthVO; import run.halo.app.model.vo.ArchiveMonthVO;
import run.halo.app.model.vo.ArchiveYearVO; import run.halo.app.model.vo.ArchiveYearVO;
import run.halo.app.model.vo.PostDetailVO; import run.halo.app.model.vo.PostDetailVO;
@ -228,4 +231,21 @@ public interface PostService extends BasePostService<Post> {
* @param postId postId must not be null * @param postId postId must not be null
*/ */
void publishVisitEvent(@NonNull Integer postId); void publishVisitEvent(@NonNull Integer postId);
/**
* Gets pre && next post.
* @param currentPost post must not be null
* @return AdjacentPostVO. it contains prePost and nextPost.
* AdjacentPostVO will not be null. But prePost and nextPost may be null.
*/
@NotNull
AdjacentPostVO getAdjacentPosts(Post currentPost);
/**
* Get Post Pageable default sort
* @Desc contains three parts. First, Top Priority; Second, From Custom index sort; Third, basic id sort
* @return
*/
@NotNull
Sort getPostDefaultSort();
} }

View File

@ -1,12 +1,32 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import static org.springframework.data.domain.Sort.Direction.DESC;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrBuilder; import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import javax.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.domain.Specification;
@ -20,31 +40,39 @@ import run.halo.app.event.logger.LogEvent;
import run.halo.app.event.post.PostVisitEvent; import run.halo.app.event.post.PostVisitEvent;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.model.dto.BaseMetaDTO; import run.halo.app.model.dto.BaseMetaDTO;
import run.halo.app.model.entity.*; import run.halo.app.model.dto.post.BasePostMinimalDTO;
import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post;
import run.halo.app.model.entity.PostCategory;
import run.halo.app.model.entity.PostComment;
import run.halo.app.model.entity.PostMeta;
import run.halo.app.model.entity.PostTag;
import run.halo.app.model.entity.Tag;
import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.LogType;
import run.halo.app.model.enums.PostPermalinkType; import run.halo.app.model.enums.PostPermalinkType;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.PostQuery; import run.halo.app.model.params.PostQuery;
import run.halo.app.model.properties.PermalinkProperties; import run.halo.app.model.properties.PermalinkProperties;
import run.halo.app.model.properties.PostProperties;
import run.halo.app.model.vo.AdjacentPostVO;
import run.halo.app.model.vo.ArchiveMonthVO; import run.halo.app.model.vo.ArchiveMonthVO;
import run.halo.app.model.vo.ArchiveYearVO; import run.halo.app.model.vo.ArchiveYearVO;
import run.halo.app.model.vo.PostDetailVO; import run.halo.app.model.vo.PostDetailVO;
import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostListVO;
import run.halo.app.repository.PostRepository; import run.halo.app.repository.PostRepository;
import run.halo.app.service.*; import run.halo.app.service.CategoryService;
import run.halo.app.service.OptionService;
import run.halo.app.service.PostCategoryService;
import run.halo.app.service.PostCommentService;
import run.halo.app.service.PostMetaService;
import run.halo.app.service.PostService;
import run.halo.app.service.PostTagService;
import run.halo.app.service.TagService;
import run.halo.app.utils.DateUtils; import run.halo.app.utils.DateUtils;
import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.MarkdownUtils;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.SlugUtils; import run.halo.app.utils.SlugUtils;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Subquery;
import java.util.*;
import java.util.stream.Collectors;
import static org.springframework.data.domain.Sort.Direction.DESC;
/** /**
* Post service implementation. * Post service implementation.
* *
@ -77,14 +105,14 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
private final OptionService optionService; private final OptionService optionService;
public PostServiceImpl(PostRepository postRepository, public PostServiceImpl(PostRepository postRepository,
TagService tagService, TagService tagService,
CategoryService categoryService, CategoryService categoryService,
PostTagService postTagService, PostTagService postTagService,
PostCategoryService postCategoryService, PostCategoryService postCategoryService,
PostCommentService postCommentService, PostCommentService postCommentService,
ApplicationEventPublisher eventPublisher, ApplicationEventPublisher eventPublisher,
PostMetaService postMetaService, PostMetaService postMetaService,
OptionService optionService) { OptionService optionService) {
super(postRepository, optionService); super(postRepository, optionService);
this.postRepository = postRepository; this.postRepository = postRepository;
this.tagService = tagService; this.tagService = tagService;
@ -121,22 +149,26 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
@Override @Override
@Transactional @Transactional
public PostDetailVO createBy(Post postToCreate, Set<Integer> tagIds, Set<Integer> categoryIds, Set<PostMeta> postMetas, boolean autoSave) { public PostDetailVO createBy(Post postToCreate, Set<Integer> tagIds, Set<Integer> categoryIds,
Set<PostMeta> postMetas, boolean autoSave) {
PostDetailVO createdPost = createOrUpdate(postToCreate, tagIds, categoryIds, postMetas); PostDetailVO createdPost = createOrUpdate(postToCreate, tagIds, categoryIds, postMetas);
if (!autoSave) { if (!autoSave) {
// Log the creation // Log the creation
LogEvent logEvent = new LogEvent(this, createdPost.getId().toString(), LogType.POST_PUBLISHED, createdPost.getTitle()); LogEvent logEvent = new LogEvent(this, createdPost.getId().toString(),
LogType.POST_PUBLISHED, createdPost.getTitle());
eventPublisher.publishEvent(logEvent); eventPublisher.publishEvent(logEvent);
} }
return createdPost; return createdPost;
} }
@Override @Override
public PostDetailVO createBy(Post postToCreate, Set<Integer> tagIds, Set<Integer> categoryIds, boolean autoSave) { public PostDetailVO createBy(Post postToCreate, Set<Integer> tagIds, Set<Integer> categoryIds,
boolean autoSave) {
PostDetailVO createdPost = createOrUpdate(postToCreate, tagIds, categoryIds, null); PostDetailVO createdPost = createOrUpdate(postToCreate, tagIds, categoryIds, null);
if (!autoSave) { if (!autoSave) {
// Log the creation // Log the creation
LogEvent logEvent = new LogEvent(this, createdPost.getId().toString(), LogType.POST_PUBLISHED, createdPost.getTitle()); LogEvent logEvent = new LogEvent(this, createdPost.getId().toString(),
LogType.POST_PUBLISHED, createdPost.getTitle());
eventPublisher.publishEvent(logEvent); eventPublisher.publishEvent(logEvent);
} }
return createdPost; return createdPost;
@ -144,13 +176,15 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
@Override @Override
@Transactional @Transactional
public PostDetailVO updateBy(Post postToUpdate, Set<Integer> tagIds, Set<Integer> categoryIds, Set<PostMeta> postMetas, boolean autoSave) { public PostDetailVO updateBy(Post postToUpdate, Set<Integer> tagIds, Set<Integer> categoryIds,
Set<PostMeta> postMetas, boolean autoSave) {
// Set edit time // Set edit time
postToUpdate.setEditTime(DateUtils.now()); postToUpdate.setEditTime(DateUtils.now());
PostDetailVO updatedPost = createOrUpdate(postToUpdate, tagIds, categoryIds, postMetas); PostDetailVO updatedPost = createOrUpdate(postToUpdate, tagIds, categoryIds, postMetas);
if (!autoSave) { if (!autoSave) {
// Log the creation // Log the creation
LogEvent logEvent = new LogEvent(this, updatedPost.getId().toString(), LogType.POST_EDITED, updatedPost.getTitle()); LogEvent logEvent = new LogEvent(this, updatedPost.getId().toString(),
LogType.POST_EDITED, updatedPost.getTitle());
eventPublisher.publishEvent(logEvent); eventPublisher.publishEvent(logEvent);
} }
return updatedPost; return updatedPost;
@ -169,7 +203,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Optional<Post> postOptional = postRepository.findBy(year, month, url); Optional<Post> postOptional = postRepository.findBy(year, month, url);
return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url)); return postOptional
.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url));
} }
@Override @Override
@ -181,7 +216,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Optional<Post> postOptional = postRepository.findBy(year, month, url, status); Optional<Post> postOptional = postRepository.findBy(year, month, url, status);
return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url)); return postOptional
.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url));
} }
@Override @Override
@ -193,7 +229,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Optional<Post> postOptional = postRepository.findBy(year, month, day, url); Optional<Post> postOptional = postRepository.findBy(year, month, day, url);
return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url)); return postOptional
.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url));
} }
@Override @Override
@ -206,7 +243,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Optional<Post> postOptional = postRepository.findBy(year, month, day, url, status); Optional<Post> postOptional = postRepository.findBy(year, month, day, url, status);
return postOptional.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url)); return postOptional
.orElseThrow(() -> new NotFoundException("查询不到该文章的信息").setErrorData(url));
} }
@Override @Override
@ -225,7 +263,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
@Override @Override
public List<ArchiveYearVO> listYearArchives() { public List<ArchiveYearVO> listYearArchives() {
// Get all posts // Get all posts
List<Post> posts = postRepository.findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime")); List<Post> posts = postRepository
.findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime"));
Map<Integer, List<Post>> yearPostMap = new HashMap<>(8); Map<Integer, List<Post>> yearPostMap = new HashMap<>(8);
@ -235,7 +274,6 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
.add(post); .add(post);
}); });
List<ArchiveYearVO> archives = new LinkedList<>(); List<ArchiveYearVO> archives = new LinkedList<>();
yearPostMap.forEach((year, postList) -> { yearPostMap.forEach((year, postList) -> {
@ -257,7 +295,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
@Override @Override
public List<ArchiveMonthVO> listMonthArchives() { public List<ArchiveMonthVO> listMonthArchives() {
// Get all posts // Get all posts
List<Post> posts = postRepository.findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime")); List<Post> posts = postRepository
.findAllByStatus(PostStatus.PUBLISHED, Sort.by(DESC, "createTime"));
Map<Integer, Map<Integer, List<Post>>> yearMonthPostMap = new HashMap<>(8); Map<Integer, Map<Integer, List<Post>>> yearMonthPostMap = new HashMap<>(8);
@ -265,7 +304,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Calendar calendar = DateUtils.convertTo(post.getCreateTime()); Calendar calendar = DateUtils.convertTo(post.getCreateTime());
yearMonthPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new HashMap<>()) yearMonthPostMap.computeIfAbsent(calendar.get(Calendar.YEAR), year -> new HashMap<>())
.computeIfAbsent((calendar.get(Calendar.MONTH) + 1), month -> new LinkedList<>()) .computeIfAbsent((calendar.get(Calendar.MONTH) + 1),
month -> new LinkedList<>())
.add(post); .add(post);
}); });
@ -384,7 +424,6 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
public String exportMarkdown(Post post) { public String exportMarkdown(Post post) {
Assert.notNull(post, "Post must not be null"); Assert.notNull(post, "Post must not be null");
StrBuilder content = new StrBuilder("---\n"); StrBuilder content = new StrBuilder("---\n");
content.append("type: ").append("post").append("\n"); content.append("type: ").append("post").append("\n");
@ -419,7 +458,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
if (postMetas.size() > 0) { if (postMetas.size() > 0) {
content.append("postMetas:").append("\n"); content.append("postMetas:").append("\n");
for (PostMeta postMeta : postMetas) { for (PostMeta postMeta : postMetas) {
content.append(" - ").append(postMeta.getKey()).append(" : ").append(postMeta.getValue()).append("\n"); content.append(" - ").append(postMeta.getKey()).append(" : ")
.append(postMeta.getValue()).append("\n");
} }
} }
@ -467,7 +507,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Post deletedPost = super.removeById(postId); Post deletedPost = super.removeById(postId);
// Log it // Log it
eventPublisher.publishEvent(new LogEvent(this, postId.toString(), LogType.POST_DELETED, deletedPost.getTitle())); eventPublisher.publishEvent(new LogEvent(this, postId.toString(), LogType.POST_DELETED,
deletedPost.getTitle()));
return deletedPost; return deletedPost;
} }
@ -484,7 +525,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Map<Integer, List<Tag>> tagListMap = postTagService.listTagListMapBy(postIds); Map<Integer, List<Tag>> tagListMap = postTagService.listTagListMapBy(postIds);
// Get category list map // Get category list map
Map<Integer, List<Category>> categoryListMap = postCategoryService.listCategoryListMap(postIds); Map<Integer, List<Category>> categoryListMap = postCategoryService
.listCategoryListMap(postIds);
// Get comment count // Get comment count
Map<Integer, Long> commentCountMap = postCommentService.countByPostIds(postIds); Map<Integer, Long> commentCountMap = postCommentService.countByPostIds(postIds);
@ -496,9 +538,11 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
PostPermalinkType permalinkType = optionService.getPostPermalinkType(); PostPermalinkType permalinkType = optionService.getPostPermalinkType();
String pathSuffix = optionService.getByPropertyOrDefault(PermalinkProperties.PATH_SUFFIX, String.class, ""); String pathSuffix = optionService
.getByPropertyOrDefault(PermalinkProperties.PATH_SUFFIX, String.class, "");
String archivesPrefix = optionService.getByPropertyOrDefault(PermalinkProperties.ARCHIVES_PREFIX, String.class, ""); String archivesPrefix = optionService
.getByPropertyOrDefault(PermalinkProperties.ARCHIVES_PREFIX, String.class, "");
return postPage.map(post -> { return postPage.map(post -> {
PostListVO postListVO = new PostListVO().convertFrom(post); PostListVO postListVO = new PostListVO().convertFrom(post);
@ -579,14 +623,15 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
/** /**
* Converts to post detail vo. * Converts to post detail vo.
* *
* @param post post must not be null * @param post post must not be null
* @param tags tags * @param tags tags
* @param categories categories * @param categories categories
* @param postMetaList postMetas * @param postMetaList postMetas
* @return post detail vo * @return post detail vo
*/ */
@NonNull @NonNull
private PostDetailVO convertTo(@NonNull Post post, @Nullable List<Tag> tags, @Nullable List<Category> categories, List<PostMeta> postMetaList) { private PostDetailVO convertTo(@NonNull Post post, @Nullable List<Tag> tags,
@Nullable List<Category> categories, List<PostMeta> postMetaList) {
Assert.notNull(post, "Post must not be null"); Assert.notNull(post, "Post must not be null");
// Convert to base detail vo // Convert to base detail vo
@ -641,17 +686,20 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
postSubquery.select(postCategoryRoot.get("postId")); postSubquery.select(postCategoryRoot.get("postId"));
postSubquery.where( postSubquery.where(
criteriaBuilder.equal(root.get("id"), postCategoryRoot.get("postId")), criteriaBuilder.equal(root.get("id"), postCategoryRoot.get("postId")),
criteriaBuilder.equal(postCategoryRoot.get("categoryId"), postQuery.getCategoryId())); criteriaBuilder.equal(postCategoryRoot.get("categoryId"),
postQuery.getCategoryId()));
predicates.add(criteriaBuilder.exists(postSubquery)); predicates.add(criteriaBuilder.exists(postSubquery));
} }
if (postQuery.getKeyword() != null) { if (postQuery.getKeyword() != null) {
// Format like condition // Format like condition
String likeCondition = String.format("%%%s%%", StringUtils.strip(postQuery.getKeyword())); String likeCondition = String
.format("%%%s%%", StringUtils.strip(postQuery.getKeyword()));
// Build like predicate // Build like predicate
Predicate titleLike = criteriaBuilder.like(root.get("title"), likeCondition); Predicate titleLike = criteriaBuilder.like(root.get("title"), likeCondition);
Predicate originalContentLike = criteriaBuilder.like(root.get("originalContent"), likeCondition); Predicate originalContentLike = criteriaBuilder
.like(root.get("originalContent"), likeCondition);
predicates.add(criteriaBuilder.or(titleLike, originalContentLike)); predicates.add(criteriaBuilder.or(titleLike, originalContentLike));
} }
@ -660,7 +708,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
}; };
} }
private PostDetailVO createOrUpdate(@NonNull Post post, Set<Integer> tagIds, Set<Integer> categoryIds, Set<PostMeta> postMetas) { private PostDetailVO createOrUpdate(@NonNull Post post, Set<Integer> tagIds,
Set<Integer> categoryIds, Set<PostMeta> postMetas) {
Assert.notNull(post, "Post param must not be null"); Assert.notNull(post, "Post param must not be null");
// Create or update post // Create or update post
@ -677,17 +726,21 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
List<Category> categories = categoryService.listAllByIds(categoryIds); List<Category> categories = categoryService.listAllByIds(categoryIds);
// Create post tags // Create post tags
List<PostTag> postTags = postTagService.mergeOrCreateByIfAbsent(post.getId(), ServiceUtils.fetchProperty(tags, Tag::getId)); List<PostTag> postTags = postTagService.mergeOrCreateByIfAbsent(post.getId(),
ServiceUtils.fetchProperty(tags, Tag::getId));
log.debug("Created post tags: [{}]", postTags); log.debug("Created post tags: [{}]", postTags);
// Create post categories // Create post categories
List<PostCategory> postCategories = postCategoryService.mergeOrCreateByIfAbsent(post.getId(), ServiceUtils.fetchProperty(categories, Category::getId)); List<PostCategory> postCategories = postCategoryService
.mergeOrCreateByIfAbsent(post.getId(),
ServiceUtils.fetchProperty(categories, Category::getId));
log.debug("Created post categories: [{}]", postCategories); log.debug("Created post categories: [{}]", postCategories);
// Create post meta data // Create post meta data
List<PostMeta> postMetaList = postMetaService.createOrUpdateByPostId(post.getId(), postMetas); List<PostMeta> postMetaList = postMetaService
.createOrUpdateByPostId(post.getId(), postMetas);
log.debug("Created post postMetas: [{}]", postMetaList); log.debug("Created post postMetas: [{}]", postMetaList);
// Convert to post detail vo // Convert to post detail vo
@ -698,4 +751,77 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
public void publishVisitEvent(Integer postId) { public void publishVisitEvent(Integer postId) {
eventPublisher.publishEvent(new PostVisitEvent(this, postId)); eventPublisher.publishEvent(new PostVisitEvent(this, postId));
} }
@Override
public @NotNull AdjacentPostVO getAdjacentPosts(Post currentPost) {
Assert.notNull(currentPost, "Post must not be null");
// get pageable post list
List<Post> postList = new ArrayList<>();
// init fist page && default page size
Integer page = 1;
Integer defaultPageSize = 500;
boolean needNext = true;
// get custom sort type
Sort sort = getPostDefaultSort();
Pageable pageable = null;
PostStatus postStatus = PostStatus.PUBLISHED;
long totalCount = countByStatus(postStatus);
while (needNext && totalCount > postList.size()) {
pageable = PageRequest
.of(page >= 1 ? page - 1 : page, defaultPageSize, sort);
Page<Post> postPage = pageBy(postStatus, pageable);
List<Post> pageablePostList = postPage.getContent();
if (pageablePostList.size() == 0) {
break;
}
postList.addAll(postPage.getContent());
if (postList.stream().filter(it -> it.getId().equals(currentPost.getId())).count() == 1
&& !postList.stream().reduce((first, second) -> second).get().getId()
.equals(currentPost.getId())) {
// contains the post && the post is not in the end
needNext = false;
}
page++;
}
if (CollectionUtils.isEmpty(postList)) {
// if post list is empty, return empty object
return AdjacentPostVO.builder().build();
}
// get current post index in post list
List<Integer> idList = postList.stream().map(Post::getId).collect(Collectors.toList());
Integer index = idList.indexOf(currentPost.getId());
if (index == -1) {
// if not found, return empty object
return AdjacentPostVO.builder().build();
}
AdjacentPostVO adjacentPostVO = new AdjacentPostVO();
//setup pre
//TODO convert POST to PostVO (with fullPath)
if (index > 0) {
adjacentPostVO.setPrePost(postList.get(index - 1));
}
// setup next
if (index < postList.size() - 1) {
adjacentPostVO.setNextPost(postList.get(index + 1));
}
return adjacentPostVO;
}
@Override
public @NotNull Sort getPostDefaultSort() {
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT)
.toString();
return Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)).and(Sort.by(DESC, "id"));
}
} }

View File

@ -6,6 +6,7 @@ import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.PageUtil; import cn.hutool.core.util.PageUtil;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@ -30,6 +31,7 @@ import run.halo.app.model.properties.PostProperties;
import run.halo.app.model.properties.StaticDeployProperties; import run.halo.app.model.properties.StaticDeployProperties;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
import run.halo.app.model.support.StaticPageFile; import run.halo.app.model.support.StaticPageFile;
import run.halo.app.model.vo.AdjacentPostVO;
import run.halo.app.model.vo.PostDetailVO; import run.halo.app.model.vo.PostDetailVO;
import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.PostListVO;
import run.halo.app.model.vo.SheetDetailVO; import run.halo.app.model.vo.SheetDetailVO;
@ -329,8 +331,10 @@ public class StaticPageServiceImpl implements StaticPageService {
for (Post post : posts) { for (Post post : posts) {
log.info("Generate archives/{}/index.html", post.getUrl()); log.info("Generate archives/{}/index.html", post.getUrl());
ModelMap model = new ModelMap(); ModelMap model = new ModelMap();
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost)); AdjacentPostVO adjacentPostVO = postService.getAdjacentPosts(post);
adjacentPostVO.getOptionalPrePost().ifPresent(prePost -> model.addAttribute("prePost", prePost));
adjacentPostVO.getOptionalNextPost().ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
List<Category> categories = postCategoryService.listCategoriesBy(post.getId()); List<Category> categories = postCategoryService.listCategoriesBy(post.getId());
List<Tag> tags = postTagService.listTagsBy(post.getId()); List<Tag> tags = postTagService.listTagsBy(post.getId());