* 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

@ -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
* *
@ -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.
* *
@ -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);
@ -586,7 +630,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
* @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());