mirror of https://github.com/halo-dev/halo
* fix: #988 * refactor: word count * refactor: word count. * fix: index posts sort field.pull/1009/head
parent
d824ccac29
commit
21ed13aff6
|
@ -31,12 +31,15 @@ public class RedisCacheStore extends AbstractStringCacheStore {
|
|||
* Cache container.
|
||||
*/
|
||||
private final static ConcurrentHashMap<String, CacheWrapper<String>> CACHE_CONTAINER = new ConcurrentHashMap<>();
|
||||
|
||||
private volatile static JedisCluster REDIS;
|
||||
protected HaloProperties haloProperties;
|
||||
|
||||
/**
|
||||
* Lock.
|
||||
*/
|
||||
private Lock lock = new ReentrantLock();
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
protected HaloProperties haloProperties;
|
||||
|
||||
public RedisCacheStore(HaloProperties haloProperties) {
|
||||
this.haloProperties = haloProperties;
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
//package run.halo.app.controller.admin.api;
|
||||
//
|
||||
//import cn.hutool.core.io.FileUtil;
|
||||
//import io.swagger.annotations.ApiOperation;
|
||||
//import org.springframework.http.HttpEntity;
|
||||
//import org.springframework.http.HttpHeaders;
|
||||
//import org.springframework.http.MediaType;
|
||||
//import org.springframework.http.ResponseEntity;
|
||||
//import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
//import org.springframework.web.bind.annotation.GetMapping;
|
||||
//import org.springframework.web.bind.annotation.PostMapping;
|
||||
//import org.springframework.web.bind.annotation.RequestMapping;
|
||||
//import org.springframework.web.bind.annotation.RestController;
|
||||
//import org.springframework.web.client.RestTemplate;
|
||||
//import run.halo.app.model.properties.NetlifyStaticDeployProperties;
|
||||
//import run.halo.app.model.support.StaticPageFile;
|
||||
//import run.halo.app.service.OptionService;
|
||||
//import run.halo.app.service.StaticPageService;
|
||||
//
|
||||
//import java.io.FileNotFoundException;
|
||||
//import java.nio.file.Path;
|
||||
//import java.util.Collections;
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// * Static page controller.
|
||||
// *
|
||||
// * @author ryanwang
|
||||
// * @date 2019-12-25
|
||||
// */
|
||||
//@RestController
|
||||
//@RequestMapping("/api/admin/static_page")
|
||||
//public class StaticPageController {
|
||||
//
|
||||
// private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
|
||||
//
|
||||
// private final OptionService optionService;
|
||||
//
|
||||
// private final RestTemplate httpsRestTemplate;
|
||||
//
|
||||
// private final StaticPageService staticPageService;
|
||||
//
|
||||
// public StaticPageController(StaticPageService staticPageService,
|
||||
// OptionService optionService,
|
||||
// RestTemplate httpsRestTemplate) {
|
||||
// this.staticPageService = staticPageService;
|
||||
// this.optionService = optionService;
|
||||
// this.httpsRestTemplate = httpsRestTemplate;
|
||||
//
|
||||
// MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
|
||||
// mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.ALL));
|
||||
// this.httpsRestTemplate.getMessageConverters().add(mappingJackson2HttpMessageConverter);
|
||||
// }
|
||||
//
|
||||
// @GetMapping
|
||||
// @ApiOperation("List static page files.")
|
||||
// public List<StaticPageFile> list() {
|
||||
// return staticPageService.listFile();
|
||||
// }
|
||||
//
|
||||
// @GetMapping("generate")
|
||||
// @ApiOperation("Generate static page files.")
|
||||
// public void generate() {
|
||||
// staticPageService.generate();
|
||||
// }
|
||||
//
|
||||
// @PostMapping("deploy")
|
||||
// @ApiOperation("Deploy static page to remove platform")
|
||||
// public void deploy() {
|
||||
// staticPageService.deploy();
|
||||
// }
|
||||
//
|
||||
// @GetMapping("netlify")
|
||||
// public void testNetlify() throws FileNotFoundException {
|
||||
// String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
|
||||
// String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
|
||||
// String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
|
||||
//
|
||||
// HttpHeaders headers = new HttpHeaders();
|
||||
//
|
||||
// headers.set("Content-Type", "application/zip");
|
||||
// headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||
//
|
||||
// Path path = staticPageService.zipStaticPagesDirectory();
|
||||
//
|
||||
// byte[] bytes = FileUtil.readBytes(path.toFile());
|
||||
//
|
||||
// HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
|
||||
//
|
||||
// ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
|
||||
// }
|
||||
//}
|
|
@ -16,7 +16,6 @@ import run.halo.app.model.entity.Tag;
|
|||
import run.halo.app.model.enums.PostEditorType;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.model.vo.AdjacentPostVO;
|
||||
import run.halo.app.model.vo.ArchiveYearVO;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.service.*;
|
||||
|
@ -96,9 +95,8 @@ public class PostModel {
|
|||
|
||||
postService.publishVisitEvent(post.getId());
|
||||
|
||||
AdjacentPostVO adjacentPostVO = postService.getAdjacentPosts(post);
|
||||
adjacentPostVO.getOptionalPrevPost().ifPresent(prevPost -> model.addAttribute("prevPost", postService.convertToDetailVo(prevPost)));
|
||||
adjacentPostVO.getOptionalNextPost().ifPresent(nextPost -> model.addAttribute("nextPost", postService.convertToDetailVo(nextPost)));
|
||||
postService.getPrevPost(post).ifPresent(prevPost -> model.addAttribute("prevPost", postService.convertToDetailVo(prevPost)));
|
||||
postService.getNextPost(post).ifPresent(nextPost -> model.addAttribute("nextPost", postService.convertToDetailVo(nextPost)));
|
||||
|
||||
List<Category> categories = postCategoryService.listCategoriesBy(post.getId());
|
||||
List<Tag> tags = postTagService.listTagsBy(post.getId());
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.service.OptionService;
|
||||
|
||||
/**
|
||||
* Git deploy handler.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GitStaticDeployHandler implements StaticDeployHandler {
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
public GitStaticDeployHandler(OptionService optionService) {
|
||||
this.optionService = optionService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(StaticDeployType type) {
|
||||
return StaticDeployType.GIT.equals(type);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.model.properties.NetlifyStaticDeployProperties;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.StaticPageService;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Netlify deploy handler.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class NetlifyStaticDeployHandler implements StaticDeployHandler {
|
||||
|
||||
private final static String DEPLOY_API = "https://api.netlify.com/api/v1/sites/%s/deploys";
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final RestTemplate httpsRestTemplate;
|
||||
|
||||
private final StaticPageService staticPageService;
|
||||
|
||||
public NetlifyStaticDeployHandler(OptionService optionService,
|
||||
RestTemplate httpsRestTemplate,
|
||||
StaticPageService staticPageService) {
|
||||
this.optionService = optionService;
|
||||
this.httpsRestTemplate = httpsRestTemplate;
|
||||
this.staticPageService = staticPageService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
String domain = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_DOMAIN).toString();
|
||||
String siteId = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_SITE_ID).toString();
|
||||
String token = optionService.getByPropertyOfNonNull(NetlifyStaticDeployProperties.NETLIFY_TOKEN).toString();
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
headers.set("Content-Type", "application/zip");
|
||||
headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token);
|
||||
|
||||
Path path = staticPageService.zipStaticPagesDirectory();
|
||||
|
||||
byte[] bytes = FileUtil.readBytes(path.toFile());
|
||||
|
||||
HttpEntity<byte[]> httpEntity = new HttpEntity<>(bytes, headers);
|
||||
|
||||
ResponseEntity<Object> responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportType(StaticDeployType type) {
|
||||
return StaticDeployType.NETLIFY.equals(type);
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
|
||||
/**
|
||||
* Static deploy handler interface class.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
public interface StaticDeployHandler {
|
||||
|
||||
/**
|
||||
* do deploy.
|
||||
*/
|
||||
void deploy();
|
||||
|
||||
/**
|
||||
* Checks if the given type is supported.
|
||||
*
|
||||
* @param type deploy type
|
||||
* @return true if supported; false or else
|
||||
*/
|
||||
boolean supportType(@Nullable StaticDeployType type);
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package run.halo.app.handler.staticdeploy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import run.halo.app.exception.FileOperationException;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Static deploy handlers.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class StaticDeployHandlers {
|
||||
|
||||
private final Collection<StaticDeployHandler> staticDeployHandlers = new LinkedList<>();
|
||||
|
||||
public StaticDeployHandlers(ApplicationContext applicationContext) {
|
||||
// Add all static deploy handler
|
||||
addStaticDeployHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* do deploy.
|
||||
*
|
||||
* @param staticDeployType static deploy type
|
||||
*/
|
||||
public void deploy(@NonNull StaticDeployType staticDeployType) {
|
||||
Assert.notNull(staticDeployType, "Static deploy type must not be null");
|
||||
|
||||
for (StaticDeployHandler staticDeployHandler : staticDeployHandlers) {
|
||||
if (staticDeployHandler.supportType(staticDeployType)) {
|
||||
staticDeployHandler.deploy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new FileOperationException("No available static deploy handler to deploy static pages").setErrorData(staticDeployType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds static deploy handlers.
|
||||
*
|
||||
* @param staticDeployHandlers static deploy handler collection
|
||||
* @return current file handlers
|
||||
*/
|
||||
@NonNull
|
||||
public StaticDeployHandlers addStaticDeployHandlers(@Nullable Collection<StaticDeployHandler> staticDeployHandlers) {
|
||||
if (!CollectionUtils.isEmpty(staticDeployHandlers)) {
|
||||
this.staticDeployHandlers.addAll(staticDeployHandlers);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package run.halo.app.model.vo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import run.halo.app.model.entity.Post;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* AdjacentPost class
|
||||
*
|
||||
* @author zhouchunjie
|
||||
* @date 2020/1/12
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class AdjacentPostVO {
|
||||
|
||||
private Post prevPost;
|
||||
private Post nextPost;
|
||||
|
||||
public Optional<Post> getOptionalPrevPost() {
|
||||
return Optional.ofNullable(this.getPrevPost());
|
||||
}
|
||||
|
||||
public Optional<Post> getOptionalNextPost() {
|
||||
return Optional.ofNullable(this.getNextPost());
|
||||
}
|
||||
|
||||
}
|
|
@ -90,6 +90,50 @@ public interface BasePostRepository<POST extends BasePost> extends BaseRepositor
|
|||
@NonNull
|
||||
Page<POST> findAllByStatusAndCreateTimeAfter(@NonNull PostStatus status, @NonNull Date createTime, @NonNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all post by status and edit time before.
|
||||
*
|
||||
* @param status status must not be null
|
||||
* @param editTime edit time must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of post
|
||||
*/
|
||||
@NonNull
|
||||
Page<POST> findAllByStatusAndEditTimeBefore(@NonNull PostStatus status, @NonNull Date editTime, @NonNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all post by status and edit time after.
|
||||
*
|
||||
* @param status status must not be null
|
||||
* @param editTime edit time must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of post
|
||||
*/
|
||||
@NonNull
|
||||
Page<POST> findAllByStatusAndEditTimeAfter(@NonNull PostStatus status, @NonNull Date editTime, @NonNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all post by status and visits before.
|
||||
*
|
||||
* @param status status must not be null
|
||||
* @param visits visits must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of post
|
||||
*/
|
||||
@NonNull
|
||||
Page<POST> findAllByStatusAndVisitsBefore(@NonNull PostStatus status, @NonNull Long visits, @NonNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all post by status and visits after.
|
||||
*
|
||||
* @param status status must not be null
|
||||
* @param visits visits must not be null
|
||||
* @param pageable page info must not be null
|
||||
* @return a page of post
|
||||
*/
|
||||
@NonNull
|
||||
Page<POST> findAllByStatusAndVisitsAfter(@NonNull PostStatus status, @NonNull Long visits, @NonNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Gets post by slug and status.
|
||||
*
|
||||
|
|
|
@ -8,7 +8,10 @@ import run.halo.app.model.entity.Post;
|
|||
import run.halo.app.model.entity.PostMeta;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.model.params.PostQuery;
|
||||
import run.halo.app.model.vo.*;
|
||||
import run.halo.app.model.vo.ArchiveMonthVO;
|
||||
import run.halo.app.model.vo.ArchiveYearVO;
|
||||
import run.halo.app.model.vo.PostDetailVO;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.service.base.BasePostService;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
@ -254,16 +257,6 @@ public interface PostService extends BasePostService<Post> {
|
|||
*/
|
||||
void publishVisitEvent(@NonNull Integer postId);
|
||||
|
||||
/**
|
||||
* Gets pre && next post.
|
||||
*
|
||||
* @param currentPost post must not be null
|
||||
* @return AdjacentPostVO. it contains prevPost and nextPost.
|
||||
* AdjacentPostVO will not be null. But prevPost and nextPost may be null.
|
||||
*/
|
||||
@NotNull
|
||||
AdjacentPostVO getAdjacentPosts(Post currentPost);
|
||||
|
||||
/**
|
||||
* Get Post Pageable default sort
|
||||
*
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package run.halo.app.service;
|
||||
|
||||
import run.halo.app.model.support.StaticPageFile;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
||||
import static run.halo.app.model.support.HaloConst.TEMP_DIR;
|
||||
import static run.halo.app.utils.HaloUtils.ensureSuffix;
|
||||
|
||||
/**
|
||||
* Static Page service interface.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-25
|
||||
*/
|
||||
public interface StaticPageService {
|
||||
|
||||
/**
|
||||
* Static page folder location.
|
||||
*/
|
||||
String PAGES_FOLDER = "static_pages";
|
||||
|
||||
|
||||
String STATIC_PAGE_PACK_DIR = ensureSuffix(TEMP_DIR, FILE_SEPARATOR) + "static-pages-pack" + FILE_SEPARATOR;
|
||||
|
||||
String[] USELESS_FILE_SUFFIX = {"ftl", "md", "yaml", "yml", "gitignore"};
|
||||
|
||||
/**
|
||||
* Generate pages.
|
||||
*/
|
||||
void generate();
|
||||
|
||||
/**
|
||||
* Deploy static pages.
|
||||
*/
|
||||
void deploy();
|
||||
|
||||
/**
|
||||
* Zip static pages directory.
|
||||
*
|
||||
* @return zip path
|
||||
*/
|
||||
Path zipStaticPagesDirectory();
|
||||
|
||||
/**
|
||||
* List file of generated static page.
|
||||
*
|
||||
* @return a list of generated static page.
|
||||
*/
|
||||
List<StaticPageFile> listFile();
|
||||
}
|
|
@ -343,7 +343,7 @@ public interface ThemeService {
|
|||
/**
|
||||
* Fetches a specific release
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param tagName release tag name must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
|
@ -353,7 +353,7 @@ public interface ThemeService {
|
|||
/**
|
||||
* Fetches a specific branch (clone)
|
||||
*
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param uri theme remote uri must not be null
|
||||
* @param branchName wanted branch must not be null
|
||||
* @return theme property
|
||||
*/
|
||||
|
|
|
@ -10,7 +10,6 @@ import run.halo.app.model.dto.post.BasePostSimpleDTO;
|
|||
import run.halo.app.model.entity.BasePost;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -86,40 +85,40 @@ public interface BasePostService<POST extends BasePost> extends CrudService<POST
|
|||
/**
|
||||
* Lists previous posts.
|
||||
*
|
||||
* @param date date must not be null
|
||||
* @param post post must not be null
|
||||
* @param size previous max post size
|
||||
* @return a list of previous post
|
||||
*/
|
||||
@NonNull
|
||||
List<POST> listPrevPosts(@NonNull Date date, int size);
|
||||
List<POST> listPrevPosts(@NonNull POST post, int size);
|
||||
|
||||
/**
|
||||
* Lits next posts.
|
||||
*
|
||||
* @param date date must not be null
|
||||
* @param post post must not be null
|
||||
* @param size next max post size
|
||||
* @return a list of next post
|
||||
*/
|
||||
@NonNull
|
||||
List<POST> listNextPosts(@NonNull Date date, int size);
|
||||
List<POST> listNextPosts(@NonNull POST post, int size);
|
||||
|
||||
/**
|
||||
* Gets previous post.
|
||||
*
|
||||
* @param date date must not be null
|
||||
* @param post post must not be null
|
||||
* @return an optional post
|
||||
*/
|
||||
@NonNull
|
||||
Optional<POST> getPrevPost(@NonNull Date date);
|
||||
Optional<POST> getPrevPost(@NonNull POST post);
|
||||
|
||||
/**
|
||||
* Gets next post.
|
||||
*
|
||||
* @param date date must not be null
|
||||
* @param post post must not be null
|
||||
* @return an optional post
|
||||
*/
|
||||
@NonNull
|
||||
Optional<POST> getNextPost(@NonNull Date date);
|
||||
Optional<POST> getNextPost(@NonNull POST post);
|
||||
|
||||
/**
|
||||
* Pages latest posts.
|
||||
|
|
|
@ -30,7 +30,10 @@ import run.halo.app.utils.HaloUtils;
|
|||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -114,35 +117,55 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
|
|||
|
||||
|
||||
@Override
|
||||
public List<POST> listPrevPosts(Date date, int size) {
|
||||
Assert.notNull(date, "Date must not be null");
|
||||
public List<POST> listPrevPosts(POST post, int size) {
|
||||
Assert.notNull(post, "Post must not be null");
|
||||
|
||||
return basePostRepository.findAllByStatusAndCreateTimeAfter(PostStatus.PUBLISHED,
|
||||
date,
|
||||
PageRequest.of(0, size, Sort.by(ASC, "createTime")))
|
||||
.getContent();
|
||||
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
|
||||
|
||||
PageRequest pageRequest = PageRequest.of(0, size, Sort.by(ASC, indexSort));
|
||||
|
||||
switch (indexSort) {
|
||||
case "createTime":
|
||||
return basePostRepository.findAllByStatusAndCreateTimeAfter(PostStatus.PUBLISHED, post.getCreateTime(), pageRequest).getContent();
|
||||
case "editTime":
|
||||
return basePostRepository.findAllByStatusAndEditTimeAfter(PostStatus.PUBLISHED, post.getEditTime(), pageRequest).getContent();
|
||||
case "visits":
|
||||
return basePostRepository.findAllByStatusAndVisitsAfter(PostStatus.PUBLISHED, post.getVisits(), pageRequest).getContent();
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<POST> listNextPosts(Date date, int size) {
|
||||
Assert.notNull(date, "Date must not be null");
|
||||
public List<POST> listNextPosts(POST post, int size) {
|
||||
Assert.notNull(post, "Post must not be null");
|
||||
|
||||
return basePostRepository.findAllByStatusAndCreateTimeBefore(PostStatus.PUBLISHED,
|
||||
date,
|
||||
PageRequest.of(0, size, Sort.by(DESC, "createTime")))
|
||||
.getContent();
|
||||
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
|
||||
|
||||
PageRequest pageRequest = PageRequest.of(0, size, Sort.by(DESC, indexSort));
|
||||
|
||||
switch (indexSort) {
|
||||
case "createTime":
|
||||
return basePostRepository.findAllByStatusAndCreateTimeBefore(PostStatus.PUBLISHED, post.getCreateTime(), pageRequest).getContent();
|
||||
case "editTime":
|
||||
return basePostRepository.findAllByStatusAndEditTimeBefore(PostStatus.PUBLISHED, post.getEditTime(), pageRequest).getContent();
|
||||
case "visits":
|
||||
return basePostRepository.findAllByStatusAndVisitsBefore(PostStatus.PUBLISHED, post.getVisits(), pageRequest).getContent();
|
||||
default:
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<POST> getPrevPost(Date date) {
|
||||
List<POST> posts = listPrevPosts(date, 1);
|
||||
public Optional<POST> getPrevPost(POST post) {
|
||||
List<POST> posts = listPrevPosts(post, 1);
|
||||
|
||||
return CollectionUtils.isEmpty(posts) ? Optional.empty() : Optional.of(posts.get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<POST> getNextPost(Date date) {
|
||||
List<POST> posts = listNextPosts(date, 1);
|
||||
public Optional<POST> getNextPost(POST post) {
|
||||
List<POST> posts = listNextPosts(post, 1);
|
||||
|
||||
return CollectionUtils.isEmpty(posts) ? Optional.empty() : Optional.of(posts.get(0));
|
||||
}
|
||||
|
@ -232,7 +255,7 @@ public abstract class BasePostServiceImpl<POST extends BasePost> extends Abstrac
|
|||
Assert.notNull(post, "Post must not be null");
|
||||
|
||||
String originalContent = post.getOriginalContent();
|
||||
originalContent = run.halo.app.utils.StringUtils.htmlToString(originalContent);
|
||||
originalContent = HaloUtils.cleanHtmlTag(originalContent);
|
||||
|
||||
post.setWordCount((long) originalContent.length());
|
||||
|
||||
|
|
|
@ -131,6 +131,6 @@ public class LinkServiceImpl extends AbstractCrudService<Link, Integer> implemen
|
|||
}
|
||||
|
||||
return links.stream().map(link -> (LinkDTO) new LinkDTO().convertFrom(link))
|
||||
.collect(Collectors.toList());
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
@ -28,7 +27,10 @@ import run.halo.app.model.enums.PostStatus;
|
|||
import run.halo.app.model.params.PostParam;
|
||||
import run.halo.app.model.params.PostQuery;
|
||||
import run.halo.app.model.properties.PostProperties;
|
||||
import run.halo.app.model.vo.*;
|
||||
import run.halo.app.model.vo.ArchiveMonthVO;
|
||||
import run.halo.app.model.vo.ArchiveYearVO;
|
||||
import run.halo.app.model.vo.PostDetailVO;
|
||||
import run.halo.app.model.vo.PostListVO;
|
||||
import run.halo.app.repository.PostRepository;
|
||||
import run.halo.app.repository.base.BasePostRepository;
|
||||
import run.halo.app.service.*;
|
||||
|
@ -561,8 +563,6 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
|
||||
postListVO.setFullPath(buildFullPath(post));
|
||||
|
||||
postListVO.setWordCount(post.getWordCount());
|
||||
|
||||
return postListVO;
|
||||
});
|
||||
}
|
||||
|
@ -621,8 +621,6 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
|
||||
postListVO.setFullPath(buildFullPath(post));
|
||||
|
||||
postListVO.setWordCount(post.getWordCount());
|
||||
|
||||
return postListVO;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
@ -804,76 +802,11 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
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
|
||||
int page = 1;
|
||||
int 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());
|
||||
int index = idList.indexOf(currentPost.getId());
|
||||
|
||||
if (index == -1) {
|
||||
// if not found, return empty object
|
||||
return AdjacentPostVO.builder().build();
|
||||
}
|
||||
|
||||
AdjacentPostVO adjacentPostVO = new AdjacentPostVO();
|
||||
|
||||
// setup pre
|
||||
if (index > 0) {
|
||||
adjacentPostVO.setPrevPost(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"));
|
||||
return Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort).and(Sort.by(DESC, "id")));
|
||||
}
|
||||
|
||||
private String buildFullPath(Post post) {
|
||||
|
|
|
@ -1,838 +0,0 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.file.FileWriter;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
|
||||
import run.halo.app.config.properties.HaloProperties;
|
||||
import run.halo.app.exception.ServiceException;
|
||||
import run.halo.app.handler.staticdeploy.StaticDeployHandlers;
|
||||
import run.halo.app.handler.theme.config.support.ThemeProperty;
|
||||
import run.halo.app.model.dto.PhotoDTO;
|
||||
import run.halo.app.model.entity.*;
|
||||
import run.halo.app.model.enums.PostStatus;
|
||||
import run.halo.app.model.enums.StaticDeployType;
|
||||
import run.halo.app.model.properties.PostProperties;
|
||||
import run.halo.app.model.properties.StaticDeployProperties;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
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.PostListVO;
|
||||
import run.halo.app.model.vo.SheetDetailVO;
|
||||
import run.halo.app.service.*;
|
||||
import run.halo.app.utils.DateTimeUtils;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
/**
|
||||
* Static Page service implementation.
|
||||
*
|
||||
* @author ryanwang
|
||||
* @date 2019-12-25
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class StaticPageServiceImpl implements StaticPageService {
|
||||
|
||||
private final Path pagesDir;
|
||||
|
||||
private final PostService postService;
|
||||
|
||||
private final PostCategoryService postCategoryService;
|
||||
|
||||
private final PostTagService postTagService;
|
||||
|
||||
private final PostMetaService postMetaService;
|
||||
|
||||
private final SheetService sheetService;
|
||||
|
||||
private final CategoryService categoryService;
|
||||
|
||||
private final TagService tagService;
|
||||
|
||||
private final LinkService linkService;
|
||||
|
||||
private final PhotoService photoService;
|
||||
|
||||
private final JournalService journalService;
|
||||
|
||||
private final ThemeService themeService;
|
||||
|
||||
private final HaloProperties haloProperties;
|
||||
|
||||
private final OptionService optionService;
|
||||
|
||||
private final FreeMarkerConfigurer freeMarkerConfigurer;
|
||||
|
||||
private final StaticDeployHandlers staticDeployHandlers;
|
||||
|
||||
public StaticPageServiceImpl(PostService postService,
|
||||
PostCategoryService postCategoryService,
|
||||
PostTagService postTagService,
|
||||
PostMetaService postMetaService,
|
||||
SheetService sheetService,
|
||||
CategoryService categoryService,
|
||||
TagService tagService,
|
||||
LinkService linkService,
|
||||
PhotoService photoService,
|
||||
JournalService journalService,
|
||||
ThemeService themeService,
|
||||
HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
FreeMarkerConfigurer freeMarkerConfigurer,
|
||||
StaticDeployHandlers staticDeployHandlers) throws IOException {
|
||||
this.postService = postService;
|
||||
this.postCategoryService = postCategoryService;
|
||||
this.postTagService = postTagService;
|
||||
this.postMetaService = postMetaService;
|
||||
this.sheetService = sheetService;
|
||||
this.categoryService = categoryService;
|
||||
this.tagService = tagService;
|
||||
this.linkService = linkService;
|
||||
this.photoService = photoService;
|
||||
this.journalService = journalService;
|
||||
this.themeService = themeService;
|
||||
this.haloProperties = haloProperties;
|
||||
this.optionService = optionService;
|
||||
this.freeMarkerConfigurer = freeMarkerConfigurer;
|
||||
this.staticDeployHandlers = staticDeployHandlers;
|
||||
|
||||
pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER);
|
||||
FileUtils.createIfAbsent(pagesDir);
|
||||
Files.createDirectories(Paths.get(STATIC_PAGE_PACK_DIR));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate() {
|
||||
try {
|
||||
this.cleanFolder();
|
||||
this.generateIndex(1);
|
||||
this.generatePost();
|
||||
this.generateArchives(1);
|
||||
this.generateSheet();
|
||||
this.generateLink();
|
||||
this.generatePhoto(1);
|
||||
this.generateCategories();
|
||||
this.generateTags();
|
||||
this.generateRss();
|
||||
this.generateAtom();
|
||||
this.generateSiteMapHtml();
|
||||
this.generateSiteMapXml();
|
||||
this.generateRobots();
|
||||
this.generateReadme();
|
||||
this.copyThemeFolder();
|
||||
this.copyUpload();
|
||||
this.copyStatic();
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException("生成静态页面失败!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deploy() {
|
||||
StaticDeployType type = getStaticDeployType();
|
||||
|
||||
staticDeployHandlers.deploy(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path zipStaticPagesDirectory() {
|
||||
try {
|
||||
String staticPagePackName = HaloConst.STATIC_PAGE_PACK_PREFIX +
|
||||
DateTimeUtils.format(LocalDateTime.now(), DateTimeUtils.HORIZONTAL_LINE_DATETIME_FORMATTER) +
|
||||
IdUtil.simpleUUID().hashCode() + ".zip";
|
||||
Path staticPageZipPath = Files.createFile(Paths.get(STATIC_PAGE_PACK_DIR, staticPagePackName));
|
||||
|
||||
FileUtils.zip(pagesDir, staticPageZipPath);
|
||||
|
||||
return staticPageZipPath;
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to zip static pages directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StaticPageFile> listFile() {
|
||||
return listStaticPageFileTree(pagesDir);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<StaticPageFile> listStaticPageFileTree(@NonNull Path topPath) {
|
||||
Assert.notNull(topPath, "Top path must not be null");
|
||||
|
||||
if (!Files.isDirectory(topPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (Stream<Path> pathStream = Files.list(topPath)) {
|
||||
List<StaticPageFile> staticPageFiles = new LinkedList<>();
|
||||
|
||||
pathStream.forEach(path -> {
|
||||
StaticPageFile staticPageFile = new StaticPageFile();
|
||||
staticPageFile.setId(IdUtil.fastSimpleUUID());
|
||||
staticPageFile.setName(path.getFileName().toString());
|
||||
staticPageFile.setIsFile(Files.isRegularFile(path));
|
||||
if (Files.isDirectory(path)) {
|
||||
staticPageFile.setChildren(listStaticPageFileTree(path));
|
||||
}
|
||||
|
||||
staticPageFiles.add(staticPageFile);
|
||||
});
|
||||
|
||||
staticPageFiles.sort(new StaticPageFile());
|
||||
return staticPageFiles;
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to list sub files", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean static pages folder
|
||||
*/
|
||||
private void cleanFolder() {
|
||||
FileUtils.deleteFolderQuietly(pagesDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate index.html and page/{page}/index.html.
|
||||
*
|
||||
* @param page current page.
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateIndex(int page) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("index.ftl")) {
|
||||
log.warn("index.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
String indexSort = optionService.getByPropertyOfNonNull(PostProperties.INDEX_SORT).toString();
|
||||
int pageSize = optionService.getPostPageSize();
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, pageSize, Sort.by(DESC, "topPriority").and(Sort.by(DESC, indexSort)));
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_index", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("index"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateIndex(postPage.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate archives/index.html and archives/page/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateArchives(int page) throws IOException, TemplateException {
|
||||
|
||||
if (!themeService.templateExists("archives.ftl")) {
|
||||
log.warn("archives.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "topPriority"));
|
||||
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> postListVos = postService.convertToListVo(postPage);
|
||||
int[] pageRainbow = PageUtil.rainbow(page, postListVos.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_archives", true);
|
||||
model.addAttribute("pageRainbow", pageRainbow);
|
||||
model.addAttribute("posts", postListVos);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("archives"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("archives/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("archives/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateArchives(postPage.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate archives/{slug}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generatePost() throws IOException, TemplateException {
|
||||
|
||||
if (!themeService.templateExists("post.ftl")) {
|
||||
log.warn("post.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Post> posts = postService.listAllBy(PostStatus.PUBLISHED);
|
||||
|
||||
for (Post post : posts) {
|
||||
log.info("Generate archives/{}/index.html", post.getSlug());
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
AdjacentPostVO adjacentPostVO = postService.getAdjacentPosts(post);
|
||||
adjacentPostVO.getOptionalPrevPost().ifPresent(prevPost -> model.addAttribute("prevPost", prevPost));
|
||||
adjacentPostVO.getOptionalNextPost().ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
|
||||
|
||||
List<Category> categories = postCategoryService.listCategoriesBy(post.getId());
|
||||
List<Tag> tags = postTagService.listTagsBy(post.getId());
|
||||
List<PostMeta> 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));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("post"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("archives/" + post.getSlug() + "/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
log.info("Generate archives/{}/index.html succeed.", post.getSlug());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate s/{slug}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSheet() throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("sheet.ftl")) {
|
||||
log.warn("sheet.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
List<Sheet> sheets = sheetService.listAllBy(PostStatus.PUBLISHED);
|
||||
for (Sheet sheet : sheets) {
|
||||
log.info("Generate s/{}/index.html", sheet.getSlug());
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
SheetDetailVO sheetDetailVO = sheetService.convertToDetailVo(sheet);
|
||||
model.addAttribute("sheet", sheetDetailVO);
|
||||
model.addAttribute("post", sheetDetailVO);
|
||||
model.addAttribute("is_sheet", true);
|
||||
|
||||
String templateName = "sheet";
|
||||
|
||||
if (themeService.templateExists(ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate() + HaloConst.SUFFIX_FTL)) {
|
||||
templateName = ThemeService.CUSTOM_SHEET_PREFIX + sheet.getTemplate();
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix(templateName));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("s/" + sheet.getSlug() + "/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate s/{}/index.html succeed.", sheet.getSlug());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate links/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateLink() throws IOException, TemplateException {
|
||||
log.info("Generate links.html");
|
||||
|
||||
if (!themeService.templateExists("links.ftl")) {
|
||||
log.warn("links.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("links"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("links/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate links.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate photos/index.html and photos/page/{page}/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generatePhoto(int page) throws IOException, TemplateException {
|
||||
log.info("Generate photos.html");
|
||||
|
||||
if (!themeService.templateExists("photos.ftl")) {
|
||||
log.warn("photos.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
Pageable pageable = PageRequest.of(page >= 1 ? page - 1 : page, 10, Sort.by(DESC, "createTime"));
|
||||
Page<PhotoDTO> photos = photoService.pageBy(pageable);
|
||||
|
||||
model.addAttribute("photos", photos);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("photos/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("photos/page/" + page + "/photos.html"), "UTF-8");
|
||||
}
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("photos"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate photos.html succeed.");
|
||||
|
||||
if (photos.hasNext()) {
|
||||
generatePhoto(photos.getNumber() + 2);
|
||||
log.info("Generate page/{}/index.html", photos.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate categories/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateCategories() throws IOException, TemplateException {
|
||||
log.info("Generate categories.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
if (!themeService.templateExists("categories.ftl")) {
|
||||
log.warn("categories.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
model.addAttribute("is_categories", true);
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("categories"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("categories/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
List<Category> categories = categoryService.listAll();
|
||||
for (Category category : categories) {
|
||||
generateCategory(1, category);
|
||||
}
|
||||
|
||||
log.info("Generate categories.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate categories/{slug}/index.html and categories/{slug}/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @param category current category
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateCategory(int page, Category category) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("category.ftl")) {
|
||||
log.warn("category.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "createTime"));
|
||||
Page<Post> postPage = postCategoryService.pagePostBy(category.getId(), PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_category", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
model.addAttribute("category", category);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("category"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("categories/" + category.getSlug() + "/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("categories/" + category.getSlug() + "/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateCategory(postPage.getNumber() + 2, category);
|
||||
log.info("Generate categories/{}/page/{}/index.html", category.getSlug(), postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags/{slug}/index.html and tags/{slug}/{page}/index.html.
|
||||
*
|
||||
* @param page current page
|
||||
* @param tag current tag
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateTag(int page, Tag tag) throws IOException, TemplateException {
|
||||
if (!themeService.templateExists("tag.ftl")) {
|
||||
log.warn("tag.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
final Pageable pageable = PageRequest.of(page - 1, optionService.getPostPageSize(), Sort.by(DESC, "createTime"));
|
||||
Page<Post> postPage = postTagService.pagePostsBy(tag.getId(), PostStatus.PUBLISHED, pageable);
|
||||
Page<PostListVO> posts = postService.convertToListVo(postPage);
|
||||
final int[] rainbow = PageUtil.rainbow(page, posts.getTotalPages(), 3);
|
||||
|
||||
model.addAttribute("is_tag", true);
|
||||
model.addAttribute("posts", posts);
|
||||
model.addAttribute("rainbow", rainbow);
|
||||
model.addAttribute("tag", tag);
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("tag"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
|
||||
FileWriter fileWriter;
|
||||
|
||||
if (page == 1) {
|
||||
fileWriter = new FileWriter(getPageFile("tags/" + tag.getSlug() + "/index.html"), "UTF-8");
|
||||
} else {
|
||||
fileWriter = new FileWriter(getPageFile("tags/" + tag.getSlug() + "/page/" + page + "/index.html"), "UTF-8");
|
||||
}
|
||||
|
||||
fileWriter.write(html);
|
||||
|
||||
if (postPage.hasNext()) {
|
||||
generateTag(postPage.getNumber() + 2, tag);
|
||||
log.info("Generate tags/{}/page/{}/index.html", tag.getSlug(), postPage.getNumber() + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags/index.html.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateTags() throws IOException, TemplateException {
|
||||
log.info("Generate tags.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
if (!themeService.templateExists("tags.ftl")) {
|
||||
log.warn("tags.ftl not found,skip!");
|
||||
return;
|
||||
}
|
||||
|
||||
model.addAttribute("is_tags", true);
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate(themeService.renderWithSuffix("tags"));
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("tags/index.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate tags.html succeed.");
|
||||
|
||||
List<Tag> tags = tagService.listAll();
|
||||
for (Tag tag : tags) {
|
||||
generateTag(1, tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate rss.xml and feed.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateRss() throws IOException, TemplateException {
|
||||
log.info("Generate rss.xml/feed.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/rss.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter rssWriter = new FileWriter(getPageFile("rss.xml"), "UTF-8");
|
||||
rssWriter.write(xml);
|
||||
|
||||
FileWriter feedWriter = new FileWriter(getPageFile("feed.xml"), "UTF-8");
|
||||
feedWriter.write(xml);
|
||||
|
||||
log.info("Generate rss.xml/feed.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate atom.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateAtom() throws IOException, TemplateException {
|
||||
log.info("Generate atom.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/atom.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("atom.xml"), "UTF-8");
|
||||
fileWriter.write(xml);
|
||||
|
||||
log.info("Generate atom.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sitemap.html
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSiteMapHtml() throws IOException, TemplateException {
|
||||
log.info("Generate sitemap.html");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/sitemap_html.ftl");
|
||||
String html = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("sitemap.html"), "UTF-8");
|
||||
fileWriter.write(html);
|
||||
|
||||
log.info("Generate sitemap.html succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sitemap.xml
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateSiteMapXml() throws IOException, TemplateException {
|
||||
log.info("Generate sitemap.xml");
|
||||
|
||||
ModelMap model = new ModelMap();
|
||||
|
||||
model.addAttribute("posts", buildPosts(buildPostPageable(optionService.getRssPageSize())));
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/sitemap_xml.ftl");
|
||||
String xml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("sitemap.xml"), "UTF-8");
|
||||
fileWriter.write(xml);
|
||||
|
||||
log.info("Generate sitemap.xml succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate robots.txt
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateRobots() throws IOException, TemplateException {
|
||||
log.info("Generate robots.txt");
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/robots.ftl");
|
||||
String txt = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("robots.txt"), "UTF-8");
|
||||
fileWriter.write(txt);
|
||||
|
||||
log.info("Generate robots.txt succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate README.md.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
* @throws TemplateException TemplateException
|
||||
*/
|
||||
private void generateReadme() throws IOException, TemplateException {
|
||||
log.info("Generate readme.md");
|
||||
|
||||
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("common/web/readme.ftl");
|
||||
String txt = FreeMarkerTemplateUtils.processTemplateIntoString(template, null);
|
||||
FileWriter fileWriter = new FileWriter(getPageFile("README.md"), "UTF-8");
|
||||
fileWriter.write(txt);
|
||||
|
||||
log.info("Generate readme.md succeed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy current theme folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyThemeFolder() throws IOException {
|
||||
ThemeProperty activatedTheme = themeService.getActivatedTheme();
|
||||
Path path = Paths.get(pagesDir.toString(), activatedTheme.getFolderName());
|
||||
FileUtils.createIfAbsent(path);
|
||||
FileUtils.copyFolder(Paths.get(activatedTheme.getThemePath()), path);
|
||||
cleanThemeFolder(Paths.get(pagesDir.toString(), activatedTheme.getFolderName()));
|
||||
}
|
||||
|
||||
private void cleanThemeFolder(Path themePath) {
|
||||
if (!Files.isDirectory(themePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (Stream<Path> pathStream = Files.list(themePath)) {
|
||||
|
||||
pathStream.forEach(path -> {
|
||||
if (!Files.isDirectory(path)) {
|
||||
for (String suffix : USELESS_FILE_SUFFIX) {
|
||||
if (suffix.contains(FileUtil.extName(path.toFile()))) {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (path.getFileName().toString().contains(".git")) {
|
||||
FileUtils.deleteFolderQuietly(path);
|
||||
} else {
|
||||
cleanThemeFolder(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("Failed to list sub files", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy upload folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyUpload() throws IOException {
|
||||
Path path = Paths.get(pagesDir.toString(), "upload");
|
||||
FileUtils.createIfAbsent(path);
|
||||
FileUtils.copyFolder(Paths.get(haloProperties.getWorkDir(), "upload"), path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy static folder.
|
||||
*
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
private void copyStatic() throws IOException {
|
||||
FileUtils.copyFolder(Paths.get(haloProperties.getWorkDir(), "static"), pagesDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build posts for feed
|
||||
*
|
||||
* @param pageable pageable
|
||||
* @return List<Post>
|
||||
*/
|
||||
private List<PostDetailVO> buildPosts(@NonNull Pageable pageable) {
|
||||
Page<Post> postPage = postService.pageBy(PostStatus.PUBLISHED, pageable);
|
||||
Page<PostDetailVO> posts = postService.convertToDetailVo(postPage);
|
||||
posts.getContent().forEach(postListVO -> {
|
||||
try {
|
||||
// Encode post slug
|
||||
postListVO.setSlug(URLEncoder.encode(postListVO.getSlug(), StandardCharsets.UTF_8.name()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
log.warn("Failed to encode url: " + postListVO.getSlug(), e);
|
||||
}
|
||||
});
|
||||
return posts.getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds page info for post.
|
||||
*
|
||||
* @param size page size
|
||||
* @return page info
|
||||
*/
|
||||
@NonNull
|
||||
private Pageable buildPostPageable(int size) {
|
||||
return PageRequest.of(0, size, Sort.by(DESC, "createTime"));
|
||||
}
|
||||
|
||||
private File getPageFile(String subPath) {
|
||||
Path path = Paths.get(pagesDir.toString(), subPath);
|
||||
return path.toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get static deploy type from options.
|
||||
*
|
||||
* @return static deploy type
|
||||
*/
|
||||
@NonNull
|
||||
private StaticDeployType getStaticDeployType() {
|
||||
return optionService.getEnumByPropertyOrDefault(StaticDeployProperties.DEPLOY_TYPE, StaticDeployType.class, StaticDeployType.GIT);
|
||||
}
|
||||
}
|
|
@ -78,19 +78,19 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
private final AtomicReference<String> activeThemeId = new AtomicReference<>();
|
||||
|
||||
/**
|
||||
* Activated theme id.
|
||||
*/
|
||||
@Nullable
|
||||
private volatile String activatedThemeId;
|
||||
|
||||
|
||||
/**
|
||||
* Activated theme property.
|
||||
*/
|
||||
private volatile ThemeProperty activatedTheme;
|
||||
|
||||
private final AtomicReference<String> activeThemeId = new AtomicReference<>();
|
||||
|
||||
public ThemeServiceImpl(HaloProperties haloProperties,
|
||||
OptionService optionService,
|
||||
AbstractStringCacheStore cacheStore,
|
||||
|
@ -358,12 +358,6 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
return activatedTheme;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Optional<ThemeProperty> fetchActivatedTheme() {
|
||||
return fetchThemePropertyBy(getActivatedThemeId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets activated theme.
|
||||
*
|
||||
|
@ -374,6 +368,12 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
this.activatedThemeId = Optional.ofNullable(activatedTheme).map(ThemeProperty::getId).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Optional<ThemeProperty> fetchActivatedTheme() {
|
||||
return fetchThemePropertyBy(getActivatedThemeId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ThemeProperty activateTheme(@NonNull String themeId) {
|
||||
|
@ -590,7 +590,7 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
public List<ThemeProperty> fetchBranches(String uri) {
|
||||
Assert.hasText(uri, "Theme remote uri must not be blank");
|
||||
|
||||
String repoUrl = StringUtils.appendIfMissingIgnoreCase(uri, ".git",".git");
|
||||
String repoUrl = StringUtils.appendIfMissingIgnoreCase(uri, ".git", ".git");
|
||||
List<String> branches = GitUtils.getAllBranches(repoUrl);
|
||||
|
||||
List<ThemeProperty> themeProperties = new ArrayList<>();
|
||||
|
@ -717,7 +717,7 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
// Get branch
|
||||
String branch = StringUtils.isBlank(themeProperty.getBranch()) ?
|
||||
DEFAULT_REMOTE_BRANCH : themeProperty.getBranch();
|
||||
DEFAULT_REMOTE_BRANCH : themeProperty.getBranch();
|
||||
|
||||
Git git = null;
|
||||
|
||||
|
@ -728,8 +728,8 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
|
||||
// Add all changes
|
||||
git.add()
|
||||
.addFilepattern(".")
|
||||
.call();
|
||||
.addFilepattern(".")
|
||||
.call();
|
||||
// Commit the changes
|
||||
git.commit().setMessage("Commit by halo automatically").call();
|
||||
|
||||
|
@ -744,31 +744,31 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
// Force to set remote name
|
||||
git.remoteRemove().setRemoteName(THEME_PROVIDER_REMOTE_NAME).call();
|
||||
RemoteConfig remoteConfig = git.remoteAdd()
|
||||
.setName(THEME_PROVIDER_REMOTE_NAME)
|
||||
.setUri(new URIish(themeProperty.getRepo()))
|
||||
.call();
|
||||
.setName(THEME_PROVIDER_REMOTE_NAME)
|
||||
.setUri(new URIish(themeProperty.getRepo()))
|
||||
.call();
|
||||
|
||||
// Check out to specified branch
|
||||
if (!StringUtils.equalsIgnoreCase(branch, git.getRepository().getBranch())) {
|
||||
boolean present = git.branchList()
|
||||
.call()
|
||||
.stream()
|
||||
.map(Ref::getName)
|
||||
.anyMatch(name -> StringUtils.equalsIgnoreCase(name, branch));
|
||||
.call()
|
||||
.stream()
|
||||
.map(Ref::getName)
|
||||
.anyMatch(name -> StringUtils.equalsIgnoreCase(name, branch));
|
||||
|
||||
git.checkout()
|
||||
.setCreateBranch(true)
|
||||
.setForced(!present)
|
||||
.setName(branch)
|
||||
.call();
|
||||
.setCreateBranch(true)
|
||||
.setForced(!present)
|
||||
.setName(branch)
|
||||
.call();
|
||||
}
|
||||
|
||||
// Pull with rebasing
|
||||
PullResult pullResult = git.pull()
|
||||
.setRemote(remoteConfig.getName())
|
||||
.setRemoteBranchName(branch)
|
||||
.setRebase(true)
|
||||
.call();
|
||||
.setRemote(remoteConfig.getName())
|
||||
.setRemoteBranchName(branch)
|
||||
.setRebase(true)
|
||||
.call();
|
||||
|
||||
if (!pullResult.isSuccessful()) {
|
||||
log.debug("Rebase result: [{}]", pullResult.getRebaseResult());
|
||||
|
@ -787,9 +787,9 @@ public class ThemeServiceImpl implements ThemeService {
|
|||
if (StringUtils.isNotEmpty(updatedThemeProperty.getRequire()) && !VersionUtil.compareVersion(HaloConst.HALO_VERSION, updatedThemeProperty.getRequire())) {
|
||||
// reset theme version
|
||||
git.reset()
|
||||
.setMode(ResetCommand.ResetType.HARD)
|
||||
.setRef(lastCommit.getName())
|
||||
.call();
|
||||
.setMode(ResetCommand.ResetType.HARD)
|
||||
.setRef(lastCommit.getName())
|
||||
.call();
|
||||
throw new ThemeNotSupportException("新版本主题仅支持 Halo " + updatedThemeProperty.getRequire() + " 以上的版本");
|
||||
}
|
||||
} finally {
|
||||
|
|
|
@ -34,17 +34,15 @@ public enum ThemePropertyScanner {
|
|||
|
||||
INSTANCE;
|
||||
|
||||
private final ThemePropertyResolver propertyResolver = new YamlThemePropertyResolver();
|
||||
|
||||
/**
|
||||
* Theme property file name.
|
||||
*/
|
||||
private static final String[] THEME_PROPERTY_FILE_NAMES = {"theme.yaml", "theme.yml"};
|
||||
|
||||
/**
|
||||
* Theme screenshots name.
|
||||
*/
|
||||
private static final String THEME_SCREENSHOTS_NAME = "screenshot";
|
||||
private final ThemePropertyResolver propertyResolver = new YamlThemePropertyResolver();
|
||||
|
||||
/**
|
||||
* Scan theme properties.
|
||||
|
|
|
@ -137,8 +137,8 @@ public class FileUtils {
|
|||
// if zip file has root file
|
||||
if (files.size() == 1 && files.get(0).isDirectory()) {
|
||||
String rootPath = files.get(0).toPath().toString();
|
||||
String rootFile = rootPath.substring(rootPath.lastIndexOf("/", rootPath.length() - 1) + 1,rootPath.length());
|
||||
List<File> propertyFiles = Arrays.asList(files.get(0).listFiles());
|
||||
String rootFile = rootPath.substring(rootPath.lastIndexOf("/") + 1);
|
||||
File[] propertyFiles = files.get(0).listFiles();
|
||||
for (File propertyFile : propertyFiles) {
|
||||
String filePath = propertyFile.toPath().toString();
|
||||
String destPath = filePath.replace(rootFile, "");
|
||||
|
|
|
@ -13,7 +13,10 @@ import org.springframework.util.Assert;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Git utilities.
|
||||
|
@ -70,11 +73,11 @@ public class GitUtils {
|
|||
Git git = null;
|
||||
try {
|
||||
git = Git.cloneRepository()
|
||||
.setURI(repoUrl)
|
||||
.setDirectory(targetPath.toFile())
|
||||
.setBranchesToClone(Collections.singletonList("refs/heads/" + branchName))
|
||||
.setBranch("refs/heads/" + branchName)
|
||||
.call();
|
||||
.setURI(repoUrl)
|
||||
.setDirectory(targetPath.toFile())
|
||||
.setBranchesToClone(Collections.singletonList("refs/heads/" + branchName))
|
||||
.setBranch("refs/heads/" + branchName)
|
||||
.call();
|
||||
} finally {
|
||||
closeQuietly(git);
|
||||
}
|
||||
|
@ -84,9 +87,9 @@ public class GitUtils {
|
|||
List<String> branches = new ArrayList<>();
|
||||
try {
|
||||
Collection<Ref> refs = Git.lsRemoteRepository()
|
||||
.setHeads(true)
|
||||
.setRemote(repoUrl)
|
||||
.call();
|
||||
.setHeads(true)
|
||||
.setRemote(repoUrl)
|
||||
.call();
|
||||
for (Ref ref : refs) {
|
||||
branches.add(ref.getName().substring(ref.getName().lastIndexOf("/") + 1));
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class GithubUtils {
|
|||
|
||||
/**
|
||||
* Get latest release
|
||||
*
|
||||
* @param uri repository url must not be null
|
||||
* @return the map object containning tagname and zipfile url
|
||||
*/
|
||||
|
@ -49,6 +50,7 @@ public class GithubUtils {
|
|||
|
||||
/**
|
||||
* Get release information
|
||||
*
|
||||
* @param uri repository url must not be null
|
||||
* @return list of tagname of releases
|
||||
*/
|
||||
|
@ -74,7 +76,8 @@ public class GithubUtils {
|
|||
|
||||
/**
|
||||
* Get release information
|
||||
* @param uri repository url must not be null
|
||||
*
|
||||
* @param uri repository url must not be null
|
||||
* @param tagName tag must not be null
|
||||
* @return the map object containning tagname and zipfile url
|
||||
*/
|
||||
|
@ -99,7 +102,8 @@ public class GithubUtils {
|
|||
|
||||
/**
|
||||
* Get the content of theme.yaml/theme.yml
|
||||
* @param uri repository url must not be null
|
||||
*
|
||||
* @param uri repository url must not be null
|
||||
* @param branch branch must not be null
|
||||
* @return content of the file
|
||||
*/
|
||||
|
@ -110,7 +114,7 @@ public class GithubUtils {
|
|||
GithubFile githubFile = new GithubFile(repoUrl, branch);
|
||||
|
||||
Thread thread = new Thread(githubFile);
|
||||
|
||||
|
||||
thread.start();
|
||||
|
||||
thread.join(10 * 1000);
|
||||
|
@ -125,18 +129,21 @@ public class GithubUtils {
|
|||
|
||||
private static class GithubRelease implements Runnable {
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private final String repoUrl;
|
||||
|
||||
/**
|
||||
* repository tag name
|
||||
*/
|
||||
private final String tagName;
|
||||
|
||||
/**
|
||||
* The return result is zip url and tag name etc.
|
||||
*/
|
||||
private HashMap<String, Object> result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
private String tagName;
|
||||
|
||||
public GithubRelease(String repoUrl, String tagName) {
|
||||
this.repoUrl = repoUrl;
|
||||
this.tagName = tagName;
|
||||
|
@ -156,8 +163,8 @@ public class GithubUtils {
|
|||
}
|
||||
|
||||
Optional<GHRelease> res = ghReleaseList.stream()
|
||||
.filter(release -> StringUtils.equalsIgnoreCase(release.getTagName(), tagName))
|
||||
.findFirst();
|
||||
.filter(release -> StringUtils.equalsIgnoreCase(release.getTagName(), tagName))
|
||||
.findFirst();
|
||||
|
||||
if (res.isPresent()) {
|
||||
GHRelease ghRelease = res.get();
|
||||
|
@ -194,10 +201,9 @@ public class GithubUtils {
|
|||
|
||||
private static class GithubReleases implements Runnable {
|
||||
|
||||
private final String repoUrl;
|
||||
private List<String> result;
|
||||
|
||||
private String repoUrl;
|
||||
|
||||
public GithubReleases(String repoUrl) {
|
||||
this.repoUrl = repoUrl;
|
||||
result = null;
|
||||
|
@ -241,16 +247,15 @@ public class GithubUtils {
|
|||
|
||||
private static class GithubLatestRelease implements Runnable {
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private final String repoUrl;
|
||||
/**
|
||||
* The return result is zip url and tag name etc.
|
||||
*/
|
||||
private HashMap<String, Object> result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
public GithubLatestRelease(String repoUrl) {
|
||||
this.repoUrl = repoUrl;
|
||||
result = null;
|
||||
|
@ -300,20 +305,18 @@ public class GithubUtils {
|
|||
|
||||
private static class GithubFile implements Runnable {
|
||||
|
||||
/**
|
||||
* result is file content
|
||||
*/
|
||||
private String result;
|
||||
|
||||
/**
|
||||
* should be in format of "username/reponame"
|
||||
*/
|
||||
private String repoUrl;
|
||||
|
||||
private final String repoUrl;
|
||||
/**
|
||||
* the branch name
|
||||
*/
|
||||
private String branch;
|
||||
private final String branch;
|
||||
/**
|
||||
* result is file content
|
||||
*/
|
||||
private String result;
|
||||
|
||||
public GithubFile(String repoUrl, String branch) {
|
||||
this.repoUrl = repoUrl;
|
||||
|
|
|
@ -51,14 +51,14 @@ public class HttpClientUtils {
|
|||
@NonNull
|
||||
public static CloseableHttpClient createHttpsClient(int timeout) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
||||
SSLContext sslContext = new SSLContextBuilder()
|
||||
.loadTrustMaterial(null, (certificate, authType) -> true)
|
||||
.build();
|
||||
.loadTrustMaterial(null, (certificate, authType) -> true)
|
||||
.build();
|
||||
|
||||
return resolveProxySetting(HttpClients.custom())
|
||||
.setSSLContext(sslContext)
|
||||
.setSSLHostnameVerifier(new NoopHostnameVerifier())
|
||||
.setDefaultRequestConfig(getRequestConfig(timeout))
|
||||
.build();
|
||||
.setSSLContext(sslContext)
|
||||
.setSSLHostnameVerifier(new NoopHostnameVerifier())
|
||||
.setDefaultRequestConfig(getRequestConfig(timeout))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ public class HttpClientUtils {
|
|||
//set proxy credentials
|
||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
credentialsProvider.setCredentials(new AuthScope(httpHost.getHostName(), httpHost.getPort()),
|
||||
new UsernamePasswordCredentials(httpProxy.get(1), httpProxy.get(2)));
|
||||
new UsernamePasswordCredentials(httpProxy.get(1), httpProxy.get(2)));
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
}
|
||||
|
@ -126,10 +126,10 @@ public class HttpClientUtils {
|
|||
*/
|
||||
private static RequestConfig getRequestConfig(int timeout) {
|
||||
return RequestConfig.custom()
|
||||
.setConnectTimeout(timeout)
|
||||
.setConnectionRequestTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.build();
|
||||
.setConnectTimeout(timeout)
|
||||
.setConnectionRequestTimeout(timeout)
|
||||
.setSocketTimeout(timeout)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -70,6 +70,17 @@ public class MarkdownUtils {
|
|||
private static final Parser PARSER = Parser.builder(OPTIONS).build();
|
||||
|
||||
private static final HtmlRenderer RENDERER = HtmlRenderer.builder(OPTIONS).build();
|
||||
private static final Pattern FRONT_MATTER = Pattern.compile("^---[\\s\\S]*?---");
|
||||
|
||||
// /**
|
||||
// * Render html document to markdown document.
|
||||
// *
|
||||
// * @param html html document
|
||||
// * @return markdown document
|
||||
// */
|
||||
// public static String renderMarkdown(String html) {
|
||||
// return FlexmarkHtmlParser.parse(html);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Render Markdown content
|
||||
|
@ -102,16 +113,6 @@ public class MarkdownUtils {
|
|||
return RENDERER.render(document);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Render html document to markdown document.
|
||||
// *
|
||||
// * @param html html document
|
||||
// * @return markdown document
|
||||
// */
|
||||
// public static String renderMarkdown(String html) {
|
||||
// return FlexmarkHtmlParser.parse(html);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Get front-matter
|
||||
*
|
||||
|
@ -125,8 +126,6 @@ public class MarkdownUtils {
|
|||
return visitor.getData();
|
||||
}
|
||||
|
||||
private static final Pattern FRONT_MATTER = Pattern.compile("^---[\\s\\S]*?---");
|
||||
|
||||
/**
|
||||
* remove front matter
|
||||
*
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @program: halo
|
||||
* @description: processing strings Utlis
|
||||
* @author: coor.top
|
||||
* @create: 2020-07-14 01:24
|
||||
**/
|
||||
public class StringUtils {
|
||||
|
||||
private StringUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* html convert to string
|
||||
*
|
||||
* @param htmlStr
|
||||
*/
|
||||
public static String htmlToString(String htmlStr) {
|
||||
htmlStr = htmlStr.trim().replaceAll("\"", "'"); //如果有双引号将其先转成单引号
|
||||
String regExScript = "<script[^>]*?>[\\s\\S]*?<\\/script>"; // 定义script的正则表达式
|
||||
String regExStyle = "<style[^>]*?>[\\s\\S]*?<\\/style>"; // 定义style的正则表达式
|
||||
String regExHtml = "<[^>]+>"; // 定义HTML标签的正则表达式
|
||||
|
||||
Pattern pScript = Pattern.compile(regExScript, Pattern.CASE_INSENSITIVE);
|
||||
Matcher mScript = pScript.matcher(htmlStr);
|
||||
htmlStr = mScript.replaceAll(""); // 过滤script标签
|
||||
|
||||
Pattern pStyle = Pattern.compile(regExStyle, Pattern.CASE_INSENSITIVE);
|
||||
Matcher mStyle = pStyle.matcher(htmlStr);
|
||||
htmlStr = mStyle.replaceAll(""); // 过滤style标签
|
||||
|
||||
Pattern pHtml = Pattern.compile(regExHtml, Pattern.CASE_INSENSITIVE);
|
||||
Matcher mHtml = pHtml.matcher(htmlStr);
|
||||
htmlStr = mHtml.replaceAll(""); // 过滤html标签
|
||||
|
||||
return htmlStr;
|
||||
}
|
||||
}
|
|
@ -86,7 +86,7 @@ class TimeBasedOneTimePasswordUtil {
|
|||
/**
|
||||
* set to the number of digits to control 0 prefix, set to 0 for no prefix
|
||||
*/
|
||||
private static int NUM_DIGITS_OUTPUT = 6;
|
||||
private static final int NUM_DIGITS_OUTPUT = 6;
|
||||
|
||||
static {
|
||||
char[] chars = new char[NUM_DIGITS_OUTPUT];
|
||||
|
|
Loading…
Reference in New Issue