commit ab80c993d389a86a0ef79c7a13c316bcc5fd32d7 parent ad4b94cdbd4d04c1981769eb827dbc4f78ade286 Author: Wim Dupont <wim@wimdupont.com> Date: Tue, 14 Feb 2023 23:08:23 +0100 added guides Former-commit-id: 40216c63cb645f0a8b6f55c9bb1cc275fbb9aec4 Diffstat:
16 files changed, 308 insertions(+), 166 deletions(-)
diff --git a/src/main/java/com/wimdupont/personalweb/PersonalWebApplication.java b/src/main/java/com/wimdupont/personalweb/PersonalWebApplication.java @@ -1,6 +1,6 @@ package com.wimdupont.personalweb; -import com.wimdupont.personalweb.service.BlogArticleGenerator; +import com.wimdupont.personalweb.service.ScheduledFileGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; @@ -14,6 +14,6 @@ public class PersonalWebApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(PersonalWebApplication.class, args); - context.getBean(BlogArticleGenerator.class).generate(); + context.getBean(ScheduledFileGenerator.class).generate(); } } diff --git a/src/main/java/com/wimdupont/personalweb/controller/BlogController.java b/src/main/java/com/wimdupont/personalweb/controller/BlogController.java @@ -21,7 +21,7 @@ public class BlogController { @GetMapping public String getBlog(Model model) { - model.addAttribute("articles", articleService.getArticles()); + model.addAttribute("articles", articleService.getFileDtoList()); return "blog"; } @@ -29,7 +29,7 @@ public class BlogController { public String getBlogArticle(@PathVariable(value = "name") String name, Model model) { String article; try { - article = articleService.getHtmlArticle(name, true); + article = articleService.getHtmlFileString(name, true); } catch (IOException e) { article = String.format("Article with name \"%s\" not found.", name); } diff --git a/src/main/java/com/wimdupont/personalweb/controller/GuideController.java b/src/main/java/com/wimdupont/personalweb/controller/GuideController.java @@ -0,0 +1,40 @@ +package com.wimdupont.personalweb.controller; + +import com.wimdupont.personalweb.service.GuideService; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.io.IOException; + +@Controller +@RequestMapping("/guides") +public class GuideController { + + private final GuideService guideService; + + public GuideController(GuideService guideService) { + this.guideService = guideService; + } + + @GetMapping + public String getGuides(Model model) { + model.addAttribute("guides", guideService.getFileDtoList()); + return "guides"; + } + + @GetMapping(value = "/{name}") + public String getGuide(@PathVariable(value = "name") String name, Model model) { + String guide; + try { + guide = guideService.getHtmlFileString(name, true); + } catch (IOException e) { + guide = String.format("Guide with name \"%s\" not found.", name); + } + model.addAttribute("guide", guide); + model.addAttribute("title", name); + return "guide"; + } +} diff --git a/src/main/java/com/wimdupont/personalweb/model/dto/AdocFileDto.java b/src/main/java/com/wimdupont/personalweb/model/dto/AdocFileDto.java @@ -0,0 +1,41 @@ +package com.wimdupont.personalweb.model.dto; + + +import java.time.LocalDate; + +public record AdocFileDto( + String name, + LocalDate date +) { + + + private AdocFileDto(Builder builder) { + this(builder.name, builder.date); + } + + public static final class Builder { + private String name; + private LocalDate date; + + private Builder() { + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Builder name(String val) { + name = val; + return this; + } + + public Builder date(LocalDate val) { + date = val; + return this; + } + + public AdocFileDto build() { + return new AdocFileDto(this); + } + } +} diff --git a/src/main/java/com/wimdupont/personalweb/model/dto/ArticleDto.java b/src/main/java/com/wimdupont/personalweb/model/dto/ArticleDto.java @@ -1,41 +0,0 @@ -package com.wimdupont.personalweb.model.dto; - - -import java.time.LocalDate; - -public record ArticleDto( - String name, - LocalDate date -) { - - - private ArticleDto(Builder builder) { - this(builder.name, builder.date); - } - - public static final class Builder { - private String name; - private LocalDate date; - - private Builder() { - } - - public static Builder newBuilder() { - return new Builder(); - } - - public Builder name(String val) { - name = val; - return this; - } - - public Builder date(LocalDate val) { - date = val; - return this; - } - - public ArticleDto build() { - return new ArticleDto(this); - } - } -} diff --git a/src/main/java/com/wimdupont/personalweb/service/AdocFileService.java b/src/main/java/com/wimdupont/personalweb/service/AdocFileService.java @@ -0,0 +1,59 @@ +package com.wimdupont.personalweb.service; + +import com.wimdupont.personalweb.model.dto.AdocFileDto; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.TimeZone; + +import static com.wimdupont.personalweb.util.Constants.ADOC_SUFFIX; +import static com.wimdupont.personalweb.util.Constants.HTML_SUFFIX; + +public abstract class AdocFileService { + + abstract String getAdocDirectory(); + + abstract String getHtmlDirectory(); + + public File[] getAdocFiles() { + return new File(getAdocDirectory()).listFiles(); + } + + public File[] getHtmlFiles() { + return new File(getHtmlDirectory()).listFiles(); + } + + public String getHtmlFileString(String name, boolean addSuffix) throws IOException { + return Files.readString(Paths.get(String.format("%s/%s%s", getHtmlDirectory(), name, + addSuffix ? HTML_SUFFIX : ""))); + } + + public String toHtmlPathName(File adocArticle) { + return String.format("%s/%s", getHtmlDirectory(), adocArticle.getName().replaceFirst(ADOC_SUFFIX, HTML_SUFFIX)); + } + + public List<AdocFileDto> getFileDtoList() { + List<AdocFileDto> articles = new ArrayList<>(); + File[] files = getHtmlFiles(); + if (files != null) { + Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); + for (File htmlArticle : files) { + articles.add(AdocFileDto.Builder.newBuilder() + .name(htmlArticle.getName().replaceFirst(HTML_SUFFIX, "")) + .date(LocalDate.ofInstant(Instant.ofEpochMilli(htmlArticle.lastModified()), TimeZone.getDefault().toZoneId())) + .build() + ); + } + } + return articles; + } + +} diff --git a/src/main/java/com/wimdupont/personalweb/service/ArticleService.java b/src/main/java/com/wimdupont/personalweb/service/ArticleService.java @@ -1,57 +1,18 @@ package com.wimdupont.personalweb.service; -import com.wimdupont.personalweb.model.dto.ArticleDto; import com.wimdupont.personalweb.util.Constants.Article; import org.springframework.stereotype.Service; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.time.Instant; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.TimeZone; - @Service -public class ArticleService { - - public File[] getAdocArticles() { - return new File(Article.ARTICLES_DIRECTORY).listFiles(); - } - - public File[] getHtmlArticles() { - return new File(Article.HTML_ARTICLES_DIRECTORY).listFiles(); - } - - public String getHtmlArticle(String name, boolean addSuffix) throws IOException { - return Files.readString(Paths.get(String.format("%s/%s%s", Article.HTML_ARTICLES_DIRECTORY, name, - addSuffix ? Article.HTML_SUFFIX : ""))); - } +public class ArticleService extends AdocFileService { - public String toHtmlPathName(File adocArticle) { - return String.format("%s/%s", - Article.HTML_ARTICLES_DIRECTORY, - adocArticle.getName().replaceFirst(Article.ADOC_SUFFIX, Article.HTML_SUFFIX)); + @Override + String getAdocDirectory() { + return Article.ARTICLES_DIRECTORY; } - public List<ArticleDto> getArticles() { - List<ArticleDto> articles = new ArrayList<>(); - File[] files = getHtmlArticles(); - if (files != null) { - Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); - for (File htmlArticle : files) { - articles.add(ArticleDto.Builder.newBuilder() - .name(htmlArticle.getName().replaceFirst(Article.HTML_SUFFIX, "")) - .date(LocalDate.ofInstant(Instant.ofEpochMilli(htmlArticle.lastModified()), TimeZone.getDefault().toZoneId())) - .build() - ); - } - } - return articles; + @Override + String getHtmlDirectory() { + return Article.HTML_ARTICLES_DIRECTORY; } - } diff --git a/src/main/java/com/wimdupont/personalweb/service/BlogArticleGenerator.java b/src/main/java/com/wimdupont/personalweb/service/BlogArticleGenerator.java @@ -1,65 +0,0 @@ -package com.wimdupont.personalweb.service; - -import com.wimdupont.personalweb.util.Constants.Article; -import jakarta.transaction.Transactional; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; - -@Service -@Transactional -public class BlogArticleGenerator { - - private static final Logger LOG = LoggerFactory.getLogger(BlogArticleGenerator.class); - private final AdocConverter adocConverter; - private final RssFeedGenerator rssFeedGenerator; - private final ArticleService articleService; - - public BlogArticleGenerator(AdocConverter adocConverter, - RssFeedGenerator rssFeedGenerator, - ArticleService articleService) { - this.adocConverter = adocConverter; - this.rssFeedGenerator = rssFeedGenerator; - this.articleService = articleService; - } - - @Scheduled(cron = "0 0 0 * * *") - public void generate() { - LOG.info("Blog article generator started."); - File[] files = articleService.getAdocArticles(); - if (files != null) { - createHtmlArticleDir(); - for (File article : files) { - String articlePathName = articleService.toHtmlPathName(article); - if (article.isFile() && article.getName().endsWith(Article.ADOC_SUFFIX) && !new File(articlePathName).exists()) { - try { - String htmlArticle = adocConverter.convert(article); - if (new File(articlePathName).createNewFile()) { - LOG.debug("Article is created: {}", articlePathName); - } - Files.writeString(Paths.get(articlePathName), htmlArticle); - LOG.info("Article [{}] has been generated.", article.getName()); - } catch (IOException e) { - LOG.error(e.getMessage(), e); - } - } - } - } - rssFeedGenerator.generate(); - } - - private void createHtmlArticleDir() { - File htmlArticlesDir = new File(Article.HTML_ARTICLES_DIRECTORY); - if (!htmlArticlesDir.exists()) { - if (htmlArticlesDir.mkdir()) { - LOG.debug("Directory is created: {}", Article.HTML_ARTICLES_DIRECTORY); - } - } - } -} diff --git a/src/main/java/com/wimdupont/personalweb/service/GuideService.java b/src/main/java/com/wimdupont/personalweb/service/GuideService.java @@ -0,0 +1,18 @@ +package com.wimdupont.personalweb.service; + +import com.wimdupont.personalweb.util.Constants.Guide; +import org.springframework.stereotype.Service; + +@Service +public class GuideService extends AdocFileService { + + @Override + String getAdocDirectory() { + return Guide.GUIDES_DIRECTORY; + } + + @Override + String getHtmlDirectory() { + return Guide.HTML_GUIDES_DIRECTORY; + } +} diff --git a/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java b/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java @@ -50,18 +50,18 @@ public class RssFeedGenerator { private List<Item> getItems() { List<Item> items = new ArrayList<>(); - File[] files = articleService.getHtmlArticles(); + File[] files = articleService.getHtmlFiles(); if (files != null) { Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); for (File article : files) { String articleString = null; try { - articleString = articleService.getHtmlArticle(article.getName(), false); + articleString = articleService.getHtmlFileString(article.getName(), false); } catch (IOException e) { LOG.error(e.getMessage(), e); } - String title = article.getName().replace(Constants.Article.HTML_SUFFIX, ""); + String title = article.getName().replace(Constants.HTML_SUFFIX, ""); Item item = genericItem(new Date(article.lastModified()), title); item.setTitle(title); Content content = new Content(); diff --git a/src/main/java/com/wimdupont/personalweb/service/ScheduledFileGenerator.java b/src/main/java/com/wimdupont/personalweb/service/ScheduledFileGenerator.java @@ -0,0 +1,74 @@ +package com.wimdupont.personalweb.service; + +import jakarta.transaction.Transactional; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static com.wimdupont.personalweb.util.Constants.ADOC_SUFFIX; + +@Service +@Transactional +public class ScheduledFileGenerator { + + private static final Logger LOG = LoggerFactory.getLogger(ScheduledFileGenerator.class); + private final AdocConverter adocConverter; + private final RssFeedGenerator rssFeedGenerator; + private final ArticleService articleService; + private final GuideService guideService; + + public ScheduledFileGenerator(AdocConverter adocConverter, + RssFeedGenerator rssFeedGenerator, + ArticleService articleService, + GuideService guideService) { + this.adocConverter = adocConverter; + this.rssFeedGenerator = rssFeedGenerator; + this.articleService = articleService; + this.guideService = guideService; + } + + @Scheduled(cron = "0 0 0 * * *") + public void generate() { + LOG.info("File generator started."); + generateHtmlFiles(articleService); + generateHtmlFiles(guideService); + rssFeedGenerator.generate(); + } + + private void generateHtmlFiles(AdocFileService adocFileService) { + File[] files = adocFileService.getAdocFiles(); + if (files != null) { + createHtmlDir(adocFileService); + for (File adocFile : files) { + String articlePathName = adocFileService.toHtmlPathName(adocFile); + if (adocFile.isFile() && adocFile.getName().endsWith(ADOC_SUFFIX) && !new File(articlePathName).exists()) { + try { + String htmlArticle = adocConverter.convert(adocFile); + if (new File(articlePathName).createNewFile()) { + LOG.debug("Html file is created: {}", articlePathName); + } + Files.writeString(Paths.get(articlePathName), htmlArticle); + LOG.info("Html file [{}] has been generated.", adocFile.getName()); + } catch (IOException e) { + LOG.error(e.getMessage(), e); + } + } + } + } + } + + private void createHtmlDir(AdocFileService adocFileService) { + File htmlArticlesDir = new File(adocFileService.getHtmlDirectory()); + if (!htmlArticlesDir.exists()) { + if (htmlArticlesDir.mkdir()) { + LOG.debug("Directory is created: {}", adocFileService.getHtmlDirectory()); + } + } + } +} diff --git a/src/main/java/com/wimdupont/personalweb/util/Constants.java b/src/main/java/com/wimdupont/personalweb/util/Constants.java @@ -2,6 +2,10 @@ package com.wimdupont.personalweb.util; public class Constants { + public static final String GPG_PUBLIC_KEY = System.getProperty("user.home") + "/personalweb/gpg/pub.asc"; + public static final String HTML_SUFFIX = ".html"; + public static final String ADOC_SUFFIX = ".adoc"; + private Constants() { } @@ -11,9 +15,15 @@ public class Constants { public static final String ARTICLES_DIRECTORY = System.getProperty("user.home") + "/personalweb/articles"; public static final String HTML_ARTICLES_DIRECTORY = ARTICLES_DIRECTORY + "/html"; - public static final String HTML_SUFFIX = ".html"; - public static final String ADOC_SUFFIX = ".adoc"; } - public static final String GPG_PUBLIC_KEY = System.getProperty("user.home") + "/personalweb/gpg/pub.asc"; + public static class Guide { + private Guide() { + } + + public static final String GUIDES_DIRECTORY = System.getProperty("user.home") + "/personalweb/guides"; + public static final String HTML_GUIDES_DIRECTORY = GUIDES_DIRECTORY + "/html"; + + } + } diff --git a/src/main/resources/static/css/adoc.css b/src/main/resources/static/css/adoc.css @@ -2,7 +2,16 @@ color: orange; } -.language-java { - display: block; +.language-java, +.language-bash, +.language-conf { + padding: 12px; + display: grid; background-color: #5555; -} -\ No newline at end of file + font-size: 120%; + overflow-block: scroll; +} + +.language-conf { + background-color: #cc9d9d54; +} diff --git a/src/main/resources/templates/guide.html b/src/main/resources/templates/guide.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head th:replace="~{header :: head}"></head> +<head> + <title th:text="${title} + ' - Wim Dupont'"></title> +</head> +<body> +<div class="container wrapper"> + <header> + <h1 th:utext="${title}"/> + <div th:insert="~{navigation}"/> + </header> + <div th:if="${guide != null}"> + <div class="adoc" th:utext="${guide}"/> + </div> +</div> +</body> +</html> diff --git a/src/main/resources/templates/guides.html b/src/main/resources/templates/guides.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head th:replace="~{header :: head}"></head> +<head> + <title>Guides - Wim Dupont</title> +</head> +<body> +<div class="container wrapper"> + <header> + <h1>Guides</h1> + <div th:insert="~{navigation}"/> + </header> + <tr th:each="guide : ${guides}"> + <p><a th:href="@{/guides/{guide}(guide=${guide.name})}">[[${guide.name}]]</a> - [[${guide.date}]]</p> + </tr> +</div> +</body> +</html> diff --git a/src/main/resources/templates/navigation.html b/src/main/resources/templates/navigation.html @@ -2,6 +2,7 @@ <a href="/">🏠 Home</a> <a href="/books">📚 Books</a> <a href="/blog">📝 Blog</a> + <a href="/guides">💁 Guides</a> <a href="/links">🔗 Links</a> <a href="/contact">📧 Contact</a> <a href="/donate">💰 Donate</a>