From f3763e80fc295f695f81500d6655e6dedcc77640 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Wed, 25 Dec 2019 22:51:54 +0800 Subject: [PATCH 1/7] feat: support generate static pages. --- .../admin/api/StaticPageController.java | 26 + .../halo/app/service/StaticPageService.java | 20 + .../run/halo/app/service/ThemeService.java | 14 + .../service/impl/StaticPageServiceImpl.java | 699 ++++++++++++++++++ .../app/service/impl/ThemeServiceImpl.java | 8 + .../resources/templates/common/web/readme.ftl | 32 + 6 files changed, 799 insertions(+) create mode 100644 src/main/java/run/halo/app/controller/admin/api/StaticPageController.java create mode 100644 src/main/java/run/halo/app/service/StaticPageService.java create mode 100644 src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java create mode 100644 src/main/resources/templates/common/web/readme.ftl diff --git a/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java new file mode 100644 index 000000000..dbabb940b --- /dev/null +++ b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java @@ -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(); + } +} diff --git a/src/main/java/run/halo/app/service/StaticPageService.java b/src/main/java/run/halo/app/service/StaticPageService.java new file mode 100644 index 000000000..31531c0ec --- /dev/null +++ b/src/main/java/run/halo/app/service/StaticPageService.java @@ -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(); +} diff --git a/src/main/java/run/halo/app/service/ThemeService.java b/src/main/java/run/halo/app/service/ThemeService.java index 01c7e8668..9aaa500da 100644 --- a/src/main/java/run/halo/app/service/ThemeService.java +++ b/src/main/java/run/halo/app/service/ThemeService.java @@ -64,6 +64,11 @@ public interface ThemeService { */ String RENDER_TEMPLATE = "themes/%s/%s"; + /** + * Render template with suffix. + */ + String RENDER_TEMPLATE_SUFFIX = "themes/%s/%s.ftl"; + /** * Theme cache key. */ @@ -231,6 +236,15 @@ public interface ThemeService { @NonNull 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. * diff --git a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java new file mode 100644 index 000000000..73656ba34 --- /dev/null +++ b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java @@ -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 postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); + Page 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 postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); + Page 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 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 categories = postCategoryService.listCategoriesBy(post.getId()); + List tags = postTagService.listTagsBy(post.getId()); + List metas = postMetaService.listBy(post.getId()); + + model.addAttribute("is_post", true); + model.addAttribute("post", postService.convertToDetailVo(post)); + model.addAttribute("categories", categories); + model.addAttribute("tags", tags); + model.addAttribute("metas", postMetaService.convertToMap(metas)); + + 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 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 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 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 postPage = postCategoryService.pagePostBy(category.getId(), PostStatus.PUBLISHED, pageable); + Page 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 postPage = postTagService.pagePostsBy(tag.getId(), PostStatus.PUBLISHED, pageable); + Page 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 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 + */ + private List buildPosts(@NonNull Pageable pageable) { + Page postPage = postService.pageBy(PostStatus.PUBLISHED, pageable); + Page 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(); + } +} diff --git a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java index c54cadbe4..b5c91cd10 100644 --- a/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/ThemeServiceImpl.java @@ -352,6 +352,14 @@ public class ThemeServiceImpl implements ThemeService { 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 public String getActivatedThemeId() { if (activatedThemeId == null) { diff --git a/src/main/resources/templates/common/web/readme.ftl b/src/main/resources/templates/common/web/readme.ftl new file mode 100644 index 000000000..6b40ed1b4 --- /dev/null +++ b/src/main/resources/templates/common/web/readme.ftl @@ -0,0 +1,32 @@ +
+ ${user.nickname!} +

${options.blog_title!}

+

+ ${context!} +

+
+ +--- + +<@postTag method="archiveYear"> +<#list archives as archive> +## ${archive.year?c} +<#list archive.posts?sort_by("createTime")?reverse as post> +- ${post.title!} + + + + +## 分类目录 +<@categoryTag method="list"> +<#list categories as category> +- ${category.name!} + + + +## 标签 +<@tagTag method="list"> +<#list tags as tag> +- ${tag.name!} + + \ No newline at end of file From a54c240d70b137f7d79d355015ffb7bde8a1be95 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Wed, 25 Dec 2019 23:39:21 +0800 Subject: [PATCH 2/7] feat: support copy static folder when generate static pages. --- .../controller/content/ContentArchiveController.java | 1 - .../halo/app/service/impl/StaticPageServiceImpl.java | 12 +++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java index 6f28a4c56..d599b9b43 100644 --- a/src/main/java/run/halo/app/controller/content/ContentArchiveController.java +++ b/src/main/java/run/halo/app/controller/content/ContentArchiveController.java @@ -15,7 +15,6 @@ import org.springframework.web.bind.annotation.*; import run.halo.app.cache.StringCacheStore; import run.halo.app.cache.lock.CacheLock; import run.halo.app.exception.ForbiddenException; -import run.halo.app.exception.NotFoundException; import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Post; import run.halo.app.model.entity.PostMeta; diff --git a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java index 73656ba34..9aeade557 100644 --- a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java @@ -128,9 +128,10 @@ public class StaticPageServiceImpl implements StaticPageService { this.generateSiteMapHtml(); this.generateSiteMapXml(); this.generateRobots(); + this.generateReadme(); this.copyThemeFolder(); this.copyUpload(); - this.generateReadme(); + this.copyStatic(); } catch (Exception e) { throw new ServiceException("生成静态页面失败!", e); } @@ -661,6 +662,15 @@ public class StaticPageServiceImpl implements StaticPageService { 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 * From 0f59e2fe04855b751548968889befa7f103ef743 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Thu, 26 Dec 2019 00:00:16 +0800 Subject: [PATCH 3/7] feat: clean static pages folder before generate. --- .../run/halo/app/service/impl/StaticPageServiceImpl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java index 9aeade557..eb55ae4fc 100644 --- a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java @@ -115,6 +115,7 @@ public class StaticPageServiceImpl implements StaticPageService { @Override public void generate() { try { + this.cleanFolder(); this.generateIndex(1); this.generatePost(); this.generateArchives(1); @@ -137,6 +138,13 @@ public class StaticPageServiceImpl implements StaticPageService { } } + /** + * Clean static pages folder + */ + private void cleanFolder() { + FileUtils.deleteFolderQuietly(pagesDir); + } + /** * Generate index.html and page/{page}/index.html. * From 83e87553b74697e862bd9a937c815900145e9b33 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Thu, 26 Dec 2019 14:21:49 +0800 Subject: [PATCH 4/7] feat: add static deploy handlers. --- .../admin/api/StaticPageController.java | 17 +++++ .../staticdeploy/GitStaticDeployHandler.java | 33 ++++++++ .../NetlifyStaticDeployHandler.java | 33 ++++++++ .../staticdeploy/StaticDeployHandler.java | 26 +++++++ .../staticdeploy/StaticDeployHandlers.java | 64 ++++++++++++++++ .../app/model/enums/StaticDeployType.java | 36 +++++++++ .../properties/GitStaticDeployProperties.java | 76 +++++++++++++++++++ .../NetlifyStaticDeployProperties.java | 56 ++++++++++++++ .../app/model/properties/PropertyEnum.java | 3 + .../properties/StaticDeployProperties.java | 45 +++++++++++ .../app/model/support/StaticPageFile.java | 35 +++++++++ .../halo/app/service/StaticPageService.java | 16 ++++ .../service/impl/StaticPageServiceImpl.java | 66 +++++++++++++++- 13 files changed, 505 insertions(+), 1 deletion(-) create mode 100644 src/main/java/run/halo/app/handler/staticdeploy/GitStaticDeployHandler.java create mode 100644 src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java create mode 100644 src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandler.java create mode 100644 src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java create mode 100644 src/main/java/run/halo/app/model/enums/StaticDeployType.java create mode 100644 src/main/java/run/halo/app/model/properties/GitStaticDeployProperties.java create mode 100644 src/main/java/run/halo/app/model/properties/NetlifyStaticDeployProperties.java create mode 100644 src/main/java/run/halo/app/model/properties/StaticDeployProperties.java create mode 100644 src/main/java/run/halo/app/model/support/StaticPageFile.java diff --git a/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java index dbabb940b..0e2b6b985 100644 --- a/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java +++ b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java @@ -1,10 +1,14 @@ package run.halo.app.controller.admin.api; +import io.swagger.annotations.ApiOperation; 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.model.support.StaticPageFile; import run.halo.app.service.StaticPageService; +import java.util.List; + /** * @author ryan0up * @date 2019/12/25 @@ -19,8 +23,21 @@ public class StaticPageController { this.staticPageService = staticPageService; } + @GetMapping + @ApiOperation("List static page files.") + public List list() { + return staticPageService.listFile(); + } + @GetMapping("generate") + @ApiOperation("Generate static page files.") public void generate() { staticPageService.generate(); } + + @GetMapping("deploy") + @ApiOperation("Deploy static page to remove platform") + public void deploy() { + staticPageService.deploy(); + } } diff --git a/src/main/java/run/halo/app/handler/staticdeploy/GitStaticDeployHandler.java b/src/main/java/run/halo/app/handler/staticdeploy/GitStaticDeployHandler.java new file mode 100644 index 000000000..0b94b0059 --- /dev/null +++ b/src/main/java/run/halo/app/handler/staticdeploy/GitStaticDeployHandler.java @@ -0,0 +1,33 @@ +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); + } +} diff --git a/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java b/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java new file mode 100644 index 000000000..f493bc486 --- /dev/null +++ b/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java @@ -0,0 +1,33 @@ +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; + +/** + * Netlify deploy handler. + * + * @author ryanwang + * @date 2019-12-26 + */ +@Slf4j +@Component +public class NetlifyStaticDeployHandler implements StaticDeployHandler { + + private final OptionService optionService; + + public NetlifyStaticDeployHandler(OptionService optionService) { + this.optionService = optionService; + } + + @Override + public void deploy() { + + } + + @Override + public boolean supportType(StaticDeployType type) { + return StaticDeployType.NETLIFY.equals(type); + } +} diff --git a/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandler.java b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandler.java new file mode 100644 index 000000000..f4da08737 --- /dev/null +++ b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandler.java @@ -0,0 +1,26 @@ +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); +} diff --git a/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java new file mode 100644 index 000000000..22ece8fa2 --- /dev/null +++ b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java @@ -0,0 +1,64 @@ +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 staticDeployHandlers = new LinkedList<>(); + + public StaticDeployHandlers(ApplicationContext applicationContext) { + // Add all file handler + addFileHandlers(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(); + } + } + + 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 addFileHandlers(@Nullable Collection staticDeployHandlers) { + if (!CollectionUtils.isEmpty(staticDeployHandlers)) { + this.staticDeployHandlers.addAll(staticDeployHandlers); + } + return this; + } +} diff --git a/src/main/java/run/halo/app/model/enums/StaticDeployType.java b/src/main/java/run/halo/app/model/enums/StaticDeployType.java new file mode 100644 index 000000000..79de7687b --- /dev/null +++ b/src/main/java/run/halo/app/model/enums/StaticDeployType.java @@ -0,0 +1,36 @@ +package run.halo.app.model.enums; + +/** + * Static deploy type. + * + * @author ryanwang + * @date 2019-12-26 + */ +public enum StaticDeployType implements ValueEnum { + + /** + * Deploy static pages in remote git repository, such as github pages,gitee pages,coding pages.etc. + */ + GIT(1), + + /** + * Deploy static pages in netlify. + */ + NETLIFY(2); + + private Integer value; + + StaticDeployType(Integer value) { + this.value = value; + } + + /** + * Get enum value. + * + * @return enum value + */ + @Override + public Integer getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/properties/GitStaticDeployProperties.java b/src/main/java/run/halo/app/model/properties/GitStaticDeployProperties.java new file mode 100644 index 000000000..e52bdc12a --- /dev/null +++ b/src/main/java/run/halo/app/model/properties/GitStaticDeployProperties.java @@ -0,0 +1,76 @@ +package run.halo.app.model.properties; + +/** + * Git static deploy properties. + * + * @author ryanwang + * @date 2019-12-26 + */ +public enum GitStaticDeployProperties implements PropertyEnum { + + /** + * Git static deploy domain. + */ + GIT_DOMAIN("git_static_deploy_domain", String.class, ""), + + /** + * Git static deploy repository. + */ + GIT_REPOSITORY("git_static_deploy_repository", String.class, ""), + + /** + * Git static deploy branch. + */ + GIT_BRANCH("git_static_deploy_branch", String.class, "master"), + + /** + * Git static deploy username. + */ + GIT_USERNAME("git_static_deploy_username", String.class, ""), + + /** + * Git static deploy email. + */ + GIT_EMAIL("git_static_deploy_email", String.class, ""), + + /** + * Git static deploy token. + */ + GIT_TOKEN("git_static_deploy_token", String.class, ""), + + /** + * Git static deploy cname. + */ + GIT_CNAME("git_static_deploy_cname", String.class, ""); + + private final String value; + + private final Class type; + + private final String defaultValue; + + GitStaticDeployProperties(String value, Class type, String defaultValue) { + this.defaultValue = defaultValue; + if (!PropertyEnum.isSupportedType(type)) { + throw new IllegalArgumentException("Unsupported blog property type: " + type); + } + + this.value = value; + this.type = type; + } + + @Override + public Class getType() { + return type; + } + + @Override + public String defaultValue() { + return defaultValue; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/properties/NetlifyStaticDeployProperties.java b/src/main/java/run/halo/app/model/properties/NetlifyStaticDeployProperties.java new file mode 100644 index 000000000..ac5574859 --- /dev/null +++ b/src/main/java/run/halo/app/model/properties/NetlifyStaticDeployProperties.java @@ -0,0 +1,56 @@ +package run.halo.app.model.properties; + +/** + * Netlify static deploy properties. + * + * @author ryanwang + * @date 2019-12-26 + */ +public enum NetlifyStaticDeployProperties implements PropertyEnum { + + /** + * Netlify static deploy domain. + */ + NETLIFY_DOMAIN("netlify_static_deploy_domain", String.class, ""), + + /** + * Netlify static deploy site id. + */ + NETLIFY_SITE_ID("netlify_static_deploy_site_id", String.class, ""), + + /** + * Netlify static deploy token. + */ + NETLIFY_TOKEN("netlify_static_deploy_token", String.class, ""); + + private final String value; + + private final Class type; + + private final String defaultValue; + + NetlifyStaticDeployProperties(String value, Class type, String defaultValue) { + this.defaultValue = defaultValue; + if (!PropertyEnum.isSupportedType(type)) { + throw new IllegalArgumentException("Unsupported blog property type: " + type); + } + + this.value = value; + this.type = type; + } + + @Override + public Class getType() { + return type; + } + + @Override + public String defaultValue() { + return defaultValue; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/properties/PropertyEnum.java b/src/main/java/run/halo/app/model/properties/PropertyEnum.java index 65a57290e..32e680ff1 100644 --- a/src/main/java/run/halo/app/model/properties/PropertyEnum.java +++ b/src/main/java/run/halo/app/model/properties/PropertyEnum.java @@ -155,6 +155,9 @@ public interface PropertyEnum extends ValueEnum { propertyEnumClasses.add(SeoProperties.class); propertyEnumClasses.add(UpOssProperties.class); propertyEnumClasses.add(ApiProperties.class); + propertyEnumClasses.add(StaticDeployProperties.class); + propertyEnumClasses.add(GitStaticDeployProperties.class); + propertyEnumClasses.add(NetlifyStaticDeployProperties.class); Map result = new HashMap<>(); diff --git a/src/main/java/run/halo/app/model/properties/StaticDeployProperties.java b/src/main/java/run/halo/app/model/properties/StaticDeployProperties.java new file mode 100644 index 000000000..f1222bed5 --- /dev/null +++ b/src/main/java/run/halo/app/model/properties/StaticDeployProperties.java @@ -0,0 +1,45 @@ +package run.halo.app.model.properties; + +import run.halo.app.model.enums.StaticDeployType; + +/** + * Static deploy properties. + * + * @author ryanwang + * @date 2019-12-26 + */ +public enum StaticDeployProperties implements PropertyEnum { + + /** + * static deploy type + */ + DEPLOY_TYPE("static_deploy_type", StaticDeployType.class, StaticDeployType.GIT.name()); + + private final String value; + + private final Class type; + + private final String defaultValue; + + + StaticDeployProperties(String value, Class type, String defaultValue) { + this.value = value; + this.type = type; + this.defaultValue = defaultValue; + } + + @Override + public Class getType() { + return type; + } + + @Override + public String defaultValue() { + return defaultValue; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/run/halo/app/model/support/StaticPageFile.java b/src/main/java/run/halo/app/model/support/StaticPageFile.java new file mode 100644 index 000000000..240db30b6 --- /dev/null +++ b/src/main/java/run/halo/app/model/support/StaticPageFile.java @@ -0,0 +1,35 @@ +package run.halo.app.model.support; + +import lombok.Data; + +import java.util.Comparator; +import java.util.List; + +/** + * Static page dto. + * + * @author ryanwang + * @date 2019-12-26 + */ +@Data +public class StaticPageFile implements Comparator { + + private String name; + + private Boolean isFile; + + private List children; + + @Override + public int compare(StaticPageFile leftFile, StaticPageFile rightFile) { + if (leftFile.isFile && !rightFile.isFile) { + return 1; + } + + if (!leftFile.isFile && rightFile.isFile) { + return -1; + } + + return leftFile.getName().compareTo(rightFile.getName()); + } +} diff --git a/src/main/java/run/halo/app/service/StaticPageService.java b/src/main/java/run/halo/app/service/StaticPageService.java index 31531c0ec..fc40e2a9c 100644 --- a/src/main/java/run/halo/app/service/StaticPageService.java +++ b/src/main/java/run/halo/app/service/StaticPageService.java @@ -1,5 +1,9 @@ package run.halo.app.service; +import run.halo.app.model.support.StaticPageFile; + +import java.util.List; + /** * Static Page service interface. * @@ -17,4 +21,16 @@ public interface StaticPageService { * Generate pages. */ void generate(); + + /** + * Deploy static pages. + */ + void deploy(); + + /** + * List file of generated static page. + * + * @return a list of generated static page. + */ + List listFile(); } diff --git a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java index eb55ae4fc..0d1824e04 100644 --- a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java @@ -10,18 +10,24 @@ 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.PostDetailVO; import run.halo.app.model.vo.PostListVO; import run.halo.app.model.vo.SheetDetailVO; @@ -33,9 +39,12 @@ 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.util.LinkedList; import java.util.List; +import java.util.stream.Stream; import static org.springframework.data.domain.Sort.Direction.DESC; @@ -79,6 +88,8 @@ public class StaticPageServiceImpl implements StaticPageService { private final FreeMarkerConfigurer freeMarkerConfigurer; + private final StaticDeployHandlers staticDeployHandlers; + public StaticPageServiceImpl(PostService postService, PostCategoryService postCategoryService, PostTagService postTagService, @@ -92,7 +103,8 @@ public class StaticPageServiceImpl implements StaticPageService { ThemeService themeService, HaloProperties haloProperties, OptionService optionService, - FreeMarkerConfigurer freeMarkerConfigurer) throws IOException { + FreeMarkerConfigurer freeMarkerConfigurer, + StaticDeployHandlers staticDeployHandlers) throws IOException { this.postService = postService; this.postCategoryService = postCategoryService; this.postTagService = postTagService; @@ -107,6 +119,7 @@ public class StaticPageServiceImpl implements StaticPageService { this.haloProperties = haloProperties; this.optionService = optionService; this.freeMarkerConfigurer = freeMarkerConfigurer; + this.staticDeployHandlers = staticDeployHandlers; pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER); FileUtils.createIfAbsent(pagesDir); @@ -138,6 +151,47 @@ public class StaticPageServiceImpl implements StaticPageService { } } + @Override + public void deploy() { + StaticDeployType type = getStaticDeployType(); + + staticDeployHandlers.deploy(type); + } + + @Override + public List listFile() { + return listStaticPageFileTree(pagesDir); + } + + @Nullable + private List listStaticPageFileTree(@NonNull Path topPath) { + Assert.notNull(topPath, "Top path must not be null"); + + if (!Files.isDirectory(topPath)) { + return null; + } + + try (Stream pathStream = Files.list(topPath)) { + List staticPageFiles = new LinkedList<>(); + + pathStream.forEach(path -> { + StaticPageFile staticPageFile = new StaticPageFile(); + 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 */ @@ -714,4 +768,14 @@ public class StaticPageServiceImpl implements StaticPageService { 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); + } } From c37f36f61edfad131008a9c6520387e32b32aa74 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Thu, 26 Dec 2019 20:17:03 +0800 Subject: [PATCH 5/7] feat: support deploy static page in netlify. --- src/main/java/run/halo/app/Application.java | 1 + .../admin/api/StaticPageController.java | 57 +++++++++++++++++-- .../migrate/CnBlogsMigrateHandler.java | 4 ++ .../app/handler/migrate/MigrateHandlers.java | 4 +- .../NetlifyStaticDeployHandler.java | 36 +++++++++++- .../staticdeploy/StaticDeployHandlers.java | 7 ++- .../run/halo/app/model/enums/MigrateType.java | 2 +- .../app/model/enums/StaticDeployType.java | 4 +- .../run/halo/app/model/support/HaloConst.java | 5 ++ .../halo/app/model/support/StaticFile.java | 2 + .../app/model/support/StaticPageFile.java | 2 + .../halo/app/service/StaticPageService.java | 17 ++++++ .../service/impl/StaticPageServiceImpl.java | 56 +++++++++++++++++- .../impl/StaticStorageServiceImpl.java | 2 + 14 files changed, 185 insertions(+), 14 deletions(-) diff --git a/src/main/java/run/halo/app/Application.java b/src/main/java/run/halo/app/Application.java index 037fe8ff9..efb1f5a1d 100755 --- a/src/main/java/run/halo/app/Application.java +++ b/src/main/java/run/halo/app/Application.java @@ -35,6 +35,7 @@ public class Application extends SpringBootServletInitializer { // Run application context = SpringApplication.run(Application.class, args); + } /** diff --git a/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java index 0e2b6b985..2a0b95a6f 100644 --- a/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java +++ b/src/main/java/run/halo/app/controller/admin/api/StaticPageController.java @@ -1,26 +1,55 @@ 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; /** - * @author ryan0up - * @date 2019/12/25 + * 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) { + 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 @@ -35,9 +64,29 @@ public class StaticPageController { staticPageService.generate(); } - @GetMapping("deploy") + @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 httpEntity = new HttpEntity<>(bytes, headers); + + ResponseEntity responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class); + } } diff --git a/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java b/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java index bdd512d6d..55f4f6930 100644 --- a/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java +++ b/src/main/java/run/halo/app/handler/migrate/CnBlogsMigrateHandler.java @@ -1,5 +1,7 @@ package run.halo.app.handler.migrate; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import run.halo.app.model.enums.MigrateType; @@ -9,6 +11,8 @@ import run.halo.app.model.enums.MigrateType; * @author ryanwang * @date 2019-10-30 */ +@Slf4j +@Component public class CnBlogsMigrateHandler implements MigrateHandler { @Override diff --git a/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java b/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java index e7ac60f11..325c8fe92 100644 --- a/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java +++ b/src/main/java/run/halo/app/handler/migrate/MigrateHandlers.java @@ -31,7 +31,7 @@ public class MigrateHandlers { public MigrateHandlers(ApplicationContext applicationContext) { // Add all migrate handler - addFileHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values()); + addMigrateHandlers(applicationContext.getBeansOfType(MigrateHandler.class).values()); } @NonNull @@ -56,7 +56,7 @@ public class MigrateHandlers { * @return current migrate handlers */ @NonNull - private MigrateHandlers addFileHandlers(@Nullable Collection migrateHandlers) { + private MigrateHandlers addMigrateHandlers(@Nullable Collection migrateHandlers) { if (!CollectionUtils.isEmpty(migrateHandlers)) { this.migrateHandlers.addAll(migrateHandlers); } diff --git a/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java b/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java index f493bc486..dddad86e1 100644 --- a/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java +++ b/src/main/java/run/halo/app/handler/staticdeploy/NetlifyStaticDeployHandler.java @@ -1,9 +1,18 @@ 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. @@ -15,15 +24,40 @@ import run.halo.app.service.OptionService; @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; - public NetlifyStaticDeployHandler(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 httpEntity = new HttpEntity<>(bytes, headers); + + ResponseEntity responseEntity = httpsRestTemplate.postForEntity(String.format(DEPLOY_API, siteId), httpEntity, Object.class); } @Override diff --git a/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java index 22ece8fa2..4a8b8138f 100644 --- a/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java +++ b/src/main/java/run/halo/app/handler/staticdeploy/StaticDeployHandlers.java @@ -26,8 +26,8 @@ public class StaticDeployHandlers { private final Collection staticDeployHandlers = new LinkedList<>(); public StaticDeployHandlers(ApplicationContext applicationContext) { - // Add all file handler - addFileHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values()); + // Add all static deploy handler + addStaticDeployHandlers(applicationContext.getBeansOfType(StaticDeployHandler.class).values()); } @@ -42,6 +42,7 @@ public class StaticDeployHandlers { for (StaticDeployHandler staticDeployHandler : staticDeployHandlers) { if (staticDeployHandler.supportType(staticDeployType)) { staticDeployHandler.deploy(); + return; } } @@ -55,7 +56,7 @@ public class StaticDeployHandlers { * @return current file handlers */ @NonNull - public StaticDeployHandlers addFileHandlers(@Nullable Collection staticDeployHandlers) { + public StaticDeployHandlers addStaticDeployHandlers(@Nullable Collection staticDeployHandlers) { if (!CollectionUtils.isEmpty(staticDeployHandlers)) { this.staticDeployHandlers.addAll(staticDeployHandlers); } diff --git a/src/main/java/run/halo/app/model/enums/MigrateType.java b/src/main/java/run/halo/app/model/enums/MigrateType.java index 975e55c51..74a952cc8 100644 --- a/src/main/java/run/halo/app/model/enums/MigrateType.java +++ b/src/main/java/run/halo/app/model/enums/MigrateType.java @@ -4,7 +4,7 @@ package run.halo.app.model.enums; * Migrate type. * * @author ryanwang - * @date : 2019-03-12 + * @date 2019-03-12 */ public enum MigrateType implements ValueEnum { diff --git a/src/main/java/run/halo/app/model/enums/StaticDeployType.java b/src/main/java/run/halo/app/model/enums/StaticDeployType.java index 79de7687b..4dcd9a50d 100644 --- a/src/main/java/run/halo/app/model/enums/StaticDeployType.java +++ b/src/main/java/run/halo/app/model/enums/StaticDeployType.java @@ -11,12 +11,12 @@ public enum StaticDeployType implements ValueEnum { /** * Deploy static pages in remote git repository, such as github pages,gitee pages,coding pages.etc. */ - GIT(1), + GIT(0), /** * Deploy static pages in netlify. */ - NETLIFY(2); + NETLIFY(1); private Integer value; diff --git a/src/main/java/run/halo/app/model/support/HaloConst.java b/src/main/java/run/halo/app/model/support/HaloConst.java index 18bd89d58..b728af898 100644 --- a/src/main/java/run/halo/app/model/support/HaloConst.java +++ b/src/main/java/run/halo/app/model/support/HaloConst.java @@ -30,6 +30,11 @@ public class HaloConst { */ public final static String HALO_BACKUP_PREFIX = "halo-backup-"; + /** + * Static pages pack prefix. + */ + public final static String STATIC_PAGE_PACK_PREFIX = "static-pages-"; + /** * Default theme name. */ diff --git a/src/main/java/run/halo/app/model/support/StaticFile.java b/src/main/java/run/halo/app/model/support/StaticFile.java index e1de4146a..c7f05e693 100644 --- a/src/main/java/run/halo/app/model/support/StaticFile.java +++ b/src/main/java/run/halo/app/model/support/StaticFile.java @@ -16,6 +16,8 @@ import java.util.List; @ToString public class StaticFile implements Comparator { + private String id; + private String name; private String path; diff --git a/src/main/java/run/halo/app/model/support/StaticPageFile.java b/src/main/java/run/halo/app/model/support/StaticPageFile.java index 240db30b6..f92139d1a 100644 --- a/src/main/java/run/halo/app/model/support/StaticPageFile.java +++ b/src/main/java/run/halo/app/model/support/StaticPageFile.java @@ -14,6 +14,8 @@ import java.util.List; @Data public class StaticPageFile implements Comparator { + private String id; + private String name; private Boolean isFile; diff --git a/src/main/java/run/halo/app/service/StaticPageService.java b/src/main/java/run/halo/app/service/StaticPageService.java index fc40e2a9c..72afcafed 100644 --- a/src/main/java/run/halo/app/service/StaticPageService.java +++ b/src/main/java/run/halo/app/service/StaticPageService.java @@ -2,8 +2,13 @@ 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. * @@ -17,6 +22,11 @@ public interface StaticPageService { */ 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. */ @@ -27,6 +37,13 @@ public interface StaticPageService { */ void deploy(); + /** + * Zip static pages directory. + * + * @return zip path + */ + Path zipStaticPagesDirectory(); + /** * List file of generated static page. * diff --git a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java index 0d1824e04..88f2e81fa 100644 --- a/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticPageServiceImpl.java @@ -1,6 +1,8 @@ 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; @@ -42,6 +44,8 @@ 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.time.format.DateTimeFormatter; import java.util.LinkedList; import java.util.List; import java.util.stream.Stream; @@ -123,6 +127,7 @@ public class StaticPageServiceImpl implements StaticPageService { pagesDir = Paths.get(haloProperties.getWorkDir(), PAGES_FOLDER); FileUtils.createIfAbsent(pagesDir); + Files.createDirectories(Paths.get(STATIC_PAGE_PACK_DIR)); } @Override @@ -158,6 +163,22 @@ public class StaticPageServiceImpl implements StaticPageService { staticDeployHandlers.deploy(type); } + @Override + public Path zipStaticPagesDirectory() { + try { + String staticPagePackName = HaloConst.STATIC_PAGE_PACK_PREFIX + + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss-")) + + 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 listFile() { return listStaticPageFileTree(pagesDir); @@ -176,6 +197,7 @@ public class StaticPageServiceImpl implements StaticPageService { 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)) { @@ -708,9 +730,41 @@ public class StaticPageServiceImpl implements StaticPageService { */ private void copyThemeFolder() throws IOException { ThemeProperty activatedTheme = themeService.getActivatedTheme(); - Path path = Paths.get(pagesDir.toString(), activatedTheme.getId()); + 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 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); + } } /** diff --git a/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java b/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java index 40ea905b4..8f16c4262 100644 --- a/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/StaticStorageServiceImpl.java @@ -1,5 +1,6 @@ package run.halo.app.service.impl; +import cn.hutool.core.util.IdUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; @@ -59,6 +60,7 @@ public class StaticStorageServiceImpl implements StaticStorageService { pathStream.forEach(path -> { StaticFile staticFile = new StaticFile(); + staticFile.setId(IdUtil.fastSimpleUUID()); staticFile.setName(path.getFileName().toString()); staticFile.setPath(path.toString()); staticFile.setRelativePath(StringUtils.removeStart(path.toString(), staticDir.toString())); From 305d9075310cb5312bd523f2f3e9f22790026b3c Mon Sep 17 00:00:00 2001 From: ruibaby Date: Sat, 28 Dec 2019 21:57:51 +0800 Subject: [PATCH 6/7] feat: support replace url method. --- .../halo/app/service/AttachmentService.java | 9 +++++++++ .../run/halo/app/service/OptionService.java | 9 +++++++++ .../run/halo/app/service/PhotoService.java | 9 +++++++++ .../halo/app/service/ThemeSettingService.java | 9 +++++++++ .../app/service/base/BaseCommentService.java | 9 +++++++++ .../app/service/base/BasePostService.java | 10 ++++++++++ .../service/impl/AttachmentServiceImpl.java | 12 ++++++++++++ .../service/impl/BaseCommentServiceImpl.java | 12 ++++++++++++ .../app/service/impl/BasePostServiceImpl.java | 19 +++++++++++++++---- .../app/service/impl/OptionServiceImpl.java | 13 +++++++++++++ .../app/service/impl/PhotoServiceImpl.java | 18 ++++++++++++++---- .../service/impl/ThemeSettingServiceImpl.java | 11 +++++++++++ 12 files changed, 132 insertions(+), 8 deletions(-) diff --git a/src/main/java/run/halo/app/service/AttachmentService.java b/src/main/java/run/halo/app/service/AttachmentService.java index a62a93c0e..c232a06cf 100644 --- a/src/main/java/run/halo/app/service/AttachmentService.java +++ b/src/main/java/run/halo/app/service/AttachmentService.java @@ -82,4 +82,13 @@ public interface AttachmentService extends CrudService { * @return list of type. */ List listAllType(); + + /** + * Replace attachment url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced attachments. + */ + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); } diff --git a/src/main/java/run/halo/app/service/OptionService.java b/src/main/java/run/halo/app/service/OptionService.java index 7f8763425..ef29ca479 100755 --- a/src/main/java/run/halo/app/service/OptionService.java +++ b/src/main/java/run/halo/app/service/OptionService.java @@ -338,6 +338,15 @@ public interface OptionService extends CrudService { */ long getBirthday(); + /** + * Replace option url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced options. + */ + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); + /** * Converts to option output dto. * diff --git a/src/main/java/run/halo/app/service/PhotoService.java b/src/main/java/run/halo/app/service/PhotoService.java index 105fd99e5..2437affbb 100644 --- a/src/main/java/run/halo/app/service/PhotoService.java +++ b/src/main/java/run/halo/app/service/PhotoService.java @@ -80,4 +80,13 @@ public interface PhotoService extends CrudService { * @return list of teams */ List listAllTeams(); + + /** + * Replace photo url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced photos. + */ + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); } diff --git a/src/main/java/run/halo/app/service/ThemeSettingService.java b/src/main/java/run/halo/app/service/ThemeSettingService.java index bec2c5af1..04816e536 100644 --- a/src/main/java/run/halo/app/service/ThemeSettingService.java +++ b/src/main/java/run/halo/app/service/ThemeSettingService.java @@ -55,4 +55,13 @@ public interface ThemeSettingService { */ @NonNull Map listAsMapBy(@NonNull String themeId); + + /** + * Replace theme setting url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced theme settings. + */ + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); } diff --git a/src/main/java/run/halo/app/service/base/BaseCommentService.java b/src/main/java/run/halo/app/service/base/BaseCommentService.java index dea641a6f..58e48ec72 100644 --- a/src/main/java/run/halo/app/service/base/BaseCommentService.java +++ b/src/main/java/run/halo/app/service/base/BaseCommentService.java @@ -285,4 +285,13 @@ public interface BaseCommentService extends CrudSer */ Page filterIpAddress(@NonNull Page commentPage); + /** + * Replace comment url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced comments. + */ + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); + } diff --git a/src/main/java/run/halo/app/service/base/BasePostService.java b/src/main/java/run/halo/app/service/base/BasePostService.java index 6c737188f..9178278b8 100644 --- a/src/main/java/run/halo/app/service/base/BasePostService.java +++ b/src/main/java/run/halo/app/service/base/BasePostService.java @@ -245,4 +245,14 @@ public interface BasePostService extends CrudService updateStatusByIds(@NonNull List ids, @NonNull PostStatus status); + + /** + * Replace post blog url in batch. + * + * @param oldUrl old blog url. + * @param newUrl new blog url. + * @return replaced posts. + */ + @NonNull + List replaceUrl(@NonNull String oldUrl, @NonNull String newUrl); } diff --git a/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java b/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java index ed19dcb83..4f1bd8d26 100644 --- a/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/AttachmentServiceImpl.java @@ -183,6 +183,18 @@ public class AttachmentServiceImpl extends AbstractCrudService replaceUrl(String oldUrl, String newUrl) { + List attachments = listAll(); + List replaced = new ArrayList<>(); + attachments.forEach(attachment -> { + attachment.setPath(attachment.getPath().replaceAll(oldUrl, newUrl)); + attachment.setThumbPath(attachment.getThumbPath().replaceAll(oldUrl, newUrl)); + replaced.add(attachment); + }); + return updateInBatch(replaced); + } + @Override public Attachment create(Attachment attachment) { Assert.notNull(attachment, "Attachment must not be null"); diff --git a/src/main/java/run/halo/app/service/impl/BaseCommentServiceImpl.java b/src/main/java/run/halo/app/service/impl/BaseCommentServiceImpl.java index f2137ad24..12cb86733 100644 --- a/src/main/java/run/halo/app/service/impl/BaseCommentServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BaseCommentServiceImpl.java @@ -584,6 +584,18 @@ public abstract class BaseCommentServiceImpl extend return commentPage; } + @Override + public List replaceUrl(String oldUrl, String newUrl) { + List comments = listAll(); + List replaced = new ArrayList<>(); + comments.forEach(comment -> { + comment.setAuthorUrl(comment.getAuthorUrl().replaceAll(oldUrl, newUrl)); + replaced.add(comment); + }); + List updated = updateInBatch(replaced); + return convertTo(updated); + } + /** * Get children comments recursively. * diff --git a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java index 43436dbaa..e4903953a 100644 --- a/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/BasePostServiceImpl.java @@ -29,10 +29,7 @@ import run.halo.app.utils.HaloUtils; import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.ServiceUtils; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -395,6 +392,20 @@ public abstract class BasePostServiceImpl extends Abstrac }).collect(Collectors.toList()); } + @Override + public List replaceUrl(String oldUrl, String newUrl) { + List posts = listAll(); + List replaced = new ArrayList<>(); + posts.forEach(post -> { + post.setThumbnail(post.getThumbnail().replaceAll(oldUrl, newUrl)); + post.setOriginalContent(post.getOriginalContent().replaceAll(oldUrl, newUrl)); + post.setFormatContent(post.getFormatContent().replaceAll(oldUrl, newUrl)); + replaced.add(post); + }); + List updated = updateInBatch(replaced); + return updated.stream().map(this::convertToDetail).collect(Collectors.toList()); + } + @Override public POST create(POST post) { // Check title diff --git a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java index 4f5c7a79b..c627307d5 100644 --- a/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/OptionServiceImpl.java @@ -36,6 +36,7 @@ import run.halo.app.utils.ValidationUtils; import javax.persistence.criteria.Predicate; import java.util.*; +import java.util.stream.Collectors; /** * OptionService implementation class @@ -460,6 +461,18 @@ public class OptionServiceImpl extends AbstractCrudService impl }); } + @Override + public List replaceUrl(String oldUrl, String newUrl) { + List