mirror of https://github.com/halo-dev/halo
feat: support generate static pages.
parent
f99daee263
commit
f3763e80fc
|
@ -0,0 +1,26 @@
|
||||||
|
package run.halo.app.controller.admin.api;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import run.halo.app.service.StaticPageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ryan0up
|
||||||
|
* @date 2019/12/25
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/admin/static_page")
|
||||||
|
public class StaticPageController {
|
||||||
|
|
||||||
|
private final StaticPageService staticPageService;
|
||||||
|
|
||||||
|
public StaticPageController(StaticPageService staticPageService) {
|
||||||
|
this.staticPageService = staticPageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("generate")
|
||||||
|
public void generate() {
|
||||||
|
staticPageService.generate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package run.halo.app.service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static Page service interface.
|
||||||
|
*
|
||||||
|
* @author ryanwang
|
||||||
|
* @date 2019-12-25
|
||||||
|
*/
|
||||||
|
public interface StaticPageService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static page folder location.
|
||||||
|
*/
|
||||||
|
String PAGES_FOLDER = "static_pages";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate pages.
|
||||||
|
*/
|
||||||
|
void generate();
|
||||||
|
}
|
|
@ -64,6 +64,11 @@ public interface ThemeService {
|
||||||
*/
|
*/
|
||||||
String RENDER_TEMPLATE = "themes/%s/%s";
|
String RENDER_TEMPLATE = "themes/%s/%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render template with suffix.
|
||||||
|
*/
|
||||||
|
String RENDER_TEMPLATE_SUFFIX = "themes/%s/%s.ftl";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Theme cache key.
|
* Theme cache key.
|
||||||
*/
|
*/
|
||||||
|
@ -231,6 +236,15 @@ public interface ThemeService {
|
||||||
@NonNull
|
@NonNull
|
||||||
String render(@NonNull String pageName);
|
String render(@NonNull String pageName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a theme page.
|
||||||
|
*
|
||||||
|
* @param pageName must not be blank
|
||||||
|
* @return full path of the theme page
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
String renderWithSuffix(@NonNull String pageName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets current theme id.
|
* Gets current theme id.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,699 @@
|
||||||
|
package run.halo.app.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.file.FileWriter;
|
||||||
|
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.stereotype.Service;
|
||||||
|
import org.springframework.ui.ModelMap;
|
||||||
|
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
|
||||||
|
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.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.properties.PostProperties;
|
||||||
|
import run.halo.app.model.support.HaloConst;
|
||||||
|
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.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.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) 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;
|
||||||
|
|
||||||
|
pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER);
|
||||||
|
FileUtils.createIfAbsent(pagesDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate() {
|
||||||
|
try {
|
||||||
|
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.copyThemeFolder();
|
||||||
|
this.copyUpload();
|
||||||
|
this.generateReadme();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("生成静态页面失败!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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/{url}/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.getUrl());
|
||||||
|
ModelMap model = new ModelMap();
|
||||||
|
postService.getNextPost(post.getCreateTime()).ifPresent(nextPost -> model.addAttribute("nextPost", nextPost));
|
||||||
|
postService.getPrePost(post.getCreateTime()).ifPresent(prePost -> model.addAttribute("prePost", prePost));
|
||||||
|
|
||||||
|
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.getUrl() + "/index.html"), "UTF-8");
|
||||||
|
fileWriter.write(html);
|
||||||
|
log.info("Generate archives/{}/index.html succeed.", post.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate s/{url}/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.getUrl());
|
||||||
|
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.getUrl() + "/index.html"), "UTF-8");
|
||||||
|
fileWriter.write(html);
|
||||||
|
|
||||||
|
log.info("Generate s/{}/index.html succeed.", sheet.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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/{slugName}/index.html and categories/{slugName}/{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.getSlugName() + "/index.html"), "UTF-8");
|
||||||
|
} else {
|
||||||
|
fileWriter = new FileWriter(getPageFile("categories/" + category.getSlugName() + "/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.getSlugName(), postPage.getNumber() + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate tags/{slugName}/index.html and tags/{slugName}/{page}/index.html.
|
||||||
|
*
|
||||||
|
* @param page current page
|
||||||
|
* @param category current category
|
||||||
|
* @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.getSlugName() + "/index.html"), "UTF-8");
|
||||||
|
} else {
|
||||||
|
fileWriter = new FileWriter(getPageFile("tags/" + tag.getSlugName() + "/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.getSlugName(), 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.getId());
|
||||||
|
FileUtils.createIfAbsent(path);
|
||||||
|
FileUtils.copyFolder(Paths.get(activatedTheme.getThemePath()), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 url
|
||||||
|
postListVO.setUrl(URLEncoder.encode(postListVO.getUrl(), StandardCharsets.UTF_8.name()));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
log.warn("Failed to encode url: " + postListVO.getUrl(), 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -352,6 +352,14 @@ public class ThemeServiceImpl implements ThemeService {
|
||||||
return String.format(RENDER_TEMPLATE, activatedTheme.getFolderName(), pageName);
|
return String.format(RENDER_TEMPLATE, activatedTheme.getFolderName(), pageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String renderWithSuffix(String pageName) {
|
||||||
|
// Get activated theme
|
||||||
|
ThemeProperty activatedTheme = getActivatedTheme();
|
||||||
|
// Build render url
|
||||||
|
return String.format(RENDER_TEMPLATE_SUFFIX, activatedTheme.getFolderName(), pageName);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getActivatedThemeId() {
|
public String getActivatedThemeId() {
|
||||||
if (activatedThemeId == null) {
|
if (activatedThemeId == null) {
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<div style="text-align:center">
|
||||||
|
<img src="${user.avatar!}" width="100" height="100" alt="${user.nickname!}">
|
||||||
|
<h3>${options.blog_title!}</h3>
|
||||||
|
<h4>
|
||||||
|
<a href="${context!}" target="_blank">${context!}</a>
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<@postTag method="archiveYear">
|
||||||
|
<#list archives as archive>
|
||||||
|
## ${archive.year?c}
|
||||||
|
<#list archive.posts?sort_by("createTime")?reverse as post>
|
||||||
|
- <a href="${context!}/archives/${post.url!}" title="${post.title!}" target="_blank">${post.title!}</a>
|
||||||
|
</#list>
|
||||||
|
</#list>
|
||||||
|
</@postTag>
|
||||||
|
|
||||||
|
## 分类目录
|
||||||
|
<@categoryTag method="list">
|
||||||
|
<#list categories as category>
|
||||||
|
- <a href="${context!}/categories/${category.slugName!}" target="_blank">${category.name!}</a>
|
||||||
|
</#list>
|
||||||
|
</@categoryTag>
|
||||||
|
|
||||||
|
## 标签
|
||||||
|
<@tagTag method="list">
|
||||||
|
<#list tags as tag>
|
||||||
|
- <a href="${context!}/tags/${tag.slugName!}" target="_blank">${tag.name!}</a>
|
||||||
|
</#list>
|
||||||
|
</@tagTag>
|
Loading…
Reference in New Issue