commit 0a9024ef808890067bf6a98cc9141cacc5e908ff parent 48caf46b450d0f29bc7316d7e5bb9d0948cf01f0 Author: WimDupont <WimDupont@users.noreply.gitlab.com> Date: Fri, 20 Aug 2021 17:28:41 +0200 blog and feed implementation update Diffstat:
18 files changed, 241 insertions(+), 231 deletions(-)
diff --git a/src/main/java/com/wimdupont/personalweb/PersonalWebApplication.java b/src/main/java/com/wimdupont/personalweb/PersonalWebApplication.java @@ -1,30 +1,25 @@ package com.wimdupont.personalweb; +import com.wimdupont.personalweb.service.BlogArticleGenerator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; + +import java.io.IOException; @SpringBootApplication +@EnableScheduling @ComponentScan public class PersonalWebApplication { public static void main(String[] args) { ApplicationContext context = SpringApplication.run(PersonalWebApplication.class, args); -// try { -// -// context.getBean(RSSFeedGenerator.class).generate(); -// -// -//// for (File article : articles) { -//// String result = context.getBean(AdocConverter.class).convert(article.getAbsolutePath()); -//// System.out.println(result); -//// } -// } catch (IOException e) { -// e.printStackTrace(); -// } catch (FeedException e) { -// e.printStackTrace(); -// } + try { + context.getBean(BlogArticleGenerator.class).generate(); + } catch (IOException e) { + e.printStackTrace(); + } } - } diff --git a/src/main/java/com/wimdupont/personalweb/controller/BlogController.java b/src/main/java/com/wimdupont/personalweb/controller/BlogController.java @@ -1,27 +1,44 @@ package com.wimdupont.personalweb.controller; -import com.wimdupont.personalweb.service.AdocConverter; -import lombok.RequiredArgsConstructor; +import com.wimdupont.personalweb.util.Constants; 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.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; @Controller -@RequiredArgsConstructor @RequestMapping("/blog") public class BlogController { - private final AdocConverter adocConverter; - - @GetMapping @SuppressWarnings("unused") - public String getBlog(Model model) throws IOException { - model.addAttribute("article1", adocConverter.convert("/adoc/article1.adoc")); + @GetMapping + public String getBlog(Model model) { + List<String> articles = new ArrayList<>(); + File[] files = new File(String.format("%s/html", Constants.ARTICLES_DIRECTORY)).listFiles(); + Arrays.sort(files, Comparator.comparingLong(File::lastModified)); + for (File htmlArticle : files) { + articles.add(htmlArticle.getName().replaceFirst(".html", "")); + } + model.addAttribute("articles", articles); return "blog"; } + @SuppressWarnings("unused") + @GetMapping(value = "/article/{name}") + public String getBlogArticle(@PathVariable(value = "name") String name, Model model) throws IOException { + model.addAttribute("article", Files.readString(Paths.get(String.format("%s/html/%s%s", Constants.ARTICLES_DIRECTORY, name, ".html")))); + model.addAttribute("title", name); + return "article"; + } } diff --git a/src/main/java/com/wimdupont/personalweb/controller/FeedController.java b/src/main/java/com/wimdupont/personalweb/controller/FeedController.java @@ -1,8 +1,9 @@ package com.wimdupont.personalweb.controller; import com.rometools.rome.feed.rss.Channel; -import com.wimdupont.personalweb.service.impl.RssFeedImpl; +import com.wimdupont.personalweb.service.RssFeedGenerator; import lombok.RequiredArgsConstructor; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -10,11 +11,11 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class FeedController { - private final RssFeedImpl rssFeed; + private final RssFeedGenerator rssFeed; @GetMapping(path = "/rss.xml") @SuppressWarnings("unused") - public Channel getRss() { - return rssFeed.rss(); + public Channel getRss(Model model) { + return rssFeed.generate(); } } diff --git a/src/main/java/com/wimdupont/personalweb/model/FeedModel.java b/src/main/java/com/wimdupont/personalweb/model/FeedModel.java @@ -0,0 +1,15 @@ +package com.wimdupont.personalweb.model; + +import lombok.Data; + +import java.util.Date; + +@Data +public class FeedModel { + + private String title; + private String url; + private String summary; + private Date createdDate; + +} diff --git a/src/main/java/com/wimdupont/personalweb/service/AdocConverter.java b/src/main/java/com/wimdupont/personalweb/service/AdocConverter.java @@ -1,8 +1,34 @@ package com.wimdupont.personalweb.service; +import lombok.extern.slf4j.Slf4j; +import org.asciidoctor.Asciidoctor; +import org.asciidoctor.Options; +import org.asciidoctor.SafeMode; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +@Service +@Transactional +@Slf4j +public class AdocConverter { -public interface AdocConverter { + private static final Asciidoctor ASCIIDOCTOR = Asciidoctor.Factory.create(); + private static final Options OPTIONS = Options.builder() + .safe(SafeMode.UNSAFE) + .build(); - String convert(String filename) throws IOException; + public String convert(File file) { + try { + String article = Files.readString(Path.of(file.getAbsolutePath())); + return ASCIIDOCTOR.convert(article, OPTIONS); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + return null; + } } diff --git a/src/main/java/com/wimdupont/personalweb/service/BlogArticleGenerator.java b/src/main/java/com/wimdupont/personalweb/service/BlogArticleGenerator.java @@ -0,0 +1,33 @@ +package com.wimdupont.personalweb.service; + +import com.wimdupont.personalweb.util.Constants; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +@Transactional +@RequiredArgsConstructor +public class BlogArticleGenerator { + + private final AdocConverter adocConverter; + + @Scheduled(cron = "0 0 0 * * *") + public void generate() throws IOException { + for (File article : new File(Constants.ARTICLES_DIRECTORY).listFiles()) { + if (article.isFile() && article.getName().endsWith(".adoc") && !new File(String.format("%s/html/%s", Constants.ARTICLES_DIRECTORY, article.getName().replace(".adoc",".html"))).exists()) { + String htmlArticle = adocConverter.convert(article); + new File(String.format("%s/html/", Constants.ARTICLES_DIRECTORY)).mkdir(); + new File(String.format("%s/html/%s", Constants.ARTICLES_DIRECTORY, article.getName().replaceFirst(".adoc", ".html"))).createNewFile(); + Files.write(Paths.get(String.format("%s/html/%s", Constants.ARTICLES_DIRECTORY, article.getName().replaceFirst(".adoc", ".html"))), htmlArticle.getBytes(StandardCharsets.UTF_8)); + } + } + } +} diff --git a/src/main/java/com/wimdupont/personalweb/service/BookService.java b/src/main/java/com/wimdupont/personalweb/service/BookService.java @@ -1,10 +1,21 @@ package com.wimdupont.personalweb.service; import com.wimdupont.personalweb.model.dao.Book; +import com.wimdupont.personalweb.repository.BookRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import javax.transaction.Transactional; import java.util.List; -public interface BookService { +@Service +@Transactional +@RequiredArgsConstructor +public class BookService { - List<Book> findAll(); + private final BookRepository bookRepository; + + public List<Book> findAll() { + return bookRepository.findAll(); + } } diff --git a/src/main/java/com/wimdupont/personalweb/service/RSSFeedGenerator.java b/src/main/java/com/wimdupont/personalweb/service/RSSFeedGenerator.java @@ -1,10 +0,0 @@ -package com.wimdupont.personalweb.service; - -import com.sun.syndication.io.FeedException; - -import java.io.IOException; - -public interface RSSFeedGenerator { - - void generate() throws IOException, FeedException; -} diff --git a/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java b/src/main/java/com/wimdupont/personalweb/service/RssFeedGenerator.java @@ -0,0 +1,79 @@ +package com.wimdupont.personalweb.service; + +import com.rometools.rome.feed.rss.Channel; +import com.rometools.rome.feed.rss.Content; +import com.rometools.rome.feed.rss.Guid; +import com.rometools.rome.feed.rss.Item; +import com.wimdupont.personalweb.util.Constants; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class RssFeedGenerator { + + public Channel generate() { + Channel channel = new Channel(); + + channel.setFeedType("rss_2.0"); + channel.setTitle("Wim Dupont"); + channel.setDescription("Updates from Wim Dupont"); + channel.setLink("https://wimdupont.com/rss.xml"); + channel.setUri("https://wimdupont.com/rss.xml"); + channel.setGenerator("Wim's Generatorâ„¢"); + channel.setPubDate(new Date()); + channel.setItems(getItems()); + + return channel; + } + + private List<Item> getItems() { + List<Item> items = new ArrayList<>(); + + File[] files = new File(String.format("%s/html", Constants.ARTICLES_DIRECTORY)).listFiles(); + Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); + for (File article : files) { + String articleString = null; + try { + articleString = Files.readString(Paths.get(String.format("%s/html/%s", Constants.ARTICLES_DIRECTORY, article.getName()))); + } catch (IOException e) { + e.printStackTrace(); + } + + String title = article.getName().replace(".html", ""); + Item item = genericItem(new Date(article.lastModified()), title); + item.setTitle(title); + Content content = new Content(); + content.setValue(articleString); + item.setContent(content); + items.add(item); + } + return items; + } + + private Item genericItem(Date postDate, String title) { + String url = String.format("https://wimdupont.com/blog/article/%s", title); + Item item = new Item(); + item.setAuthor("Wim Dupont"); + item.setLink(url); + item.setTitle(title); + item.setUri(url); + Guid guid = new Guid(); + guid.setValue(title + "-" +postDate.getTime()); + item.setGuid(guid); + item.setPubDate(postDate); + return item; + } +} diff --git a/src/main/java/com/wimdupont/personalweb/service/impl/AdocConverterImpl.java b/src/main/java/com/wimdupont/personalweb/service/impl/AdocConverterImpl.java @@ -1,31 +0,0 @@ -package com.wimdupont.personalweb.service.impl; - -import com.wimdupont.personalweb.service.AdocConverter; -import org.apache.commons.io.IOUtils; -import org.asciidoctor.Asciidoctor; -import org.asciidoctor.Options; -import org.asciidoctor.SafeMode; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -@Service -@Transactional -public class AdocConverterImpl implements AdocConverter { - - private static final Asciidoctor ASCIIDOCTOR = Asciidoctor.Factory.create(); - private static final Options OPTIONS = Options.builder().safe(SafeMode.UNSAFE).build(); - - @Override - public String convert(String filename) throws IOException { - return ASCIIDOCTOR.convert(adocToString(filename), OPTIONS); - } - - private String adocToString(String filename) throws IOException { - InputStream inputStream = getClass().getResourceAsStream(filename); - return IOUtils.toString(inputStream, StandardCharsets.UTF_8); - } -} diff --git a/src/main/java/com/wimdupont/personalweb/service/impl/BookServiceImpl.java b/src/main/java/com/wimdupont/personalweb/service/impl/BookServiceImpl.java @@ -1,23 +0,0 @@ -package com.wimdupont.personalweb.service.impl; - -import com.wimdupont.personalweb.model.dao.Book; -import com.wimdupont.personalweb.repository.BookRepository; -import com.wimdupont.personalweb.service.BookService; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.List; - -@Service -@Transactional -@RequiredArgsConstructor -public class BookServiceImpl implements BookService { - - private final BookRepository bookRepository; - - @Override - public List<Book> findAll() { - return bookRepository.findAll(); - } -} diff --git a/src/main/java/com/wimdupont/personalweb/service/impl/RSSFeedGeneratorImpl.java b/src/main/java/com/wimdupont/personalweb/service/impl/RSSFeedGeneratorImpl.java @@ -1,56 +0,0 @@ -package com.wimdupont.personalweb.service.impl; - -import com.sun.syndication.feed.synd.SyndContent; -import com.sun.syndication.feed.synd.SyndContentImpl; -import com.sun.syndication.feed.synd.SyndEntry; -import com.sun.syndication.feed.synd.SyndEntryImpl; -import com.sun.syndication.feed.synd.SyndFeed; -import com.sun.syndication.feed.synd.SyndFeedImpl; -import com.sun.syndication.io.FeedException; -import com.sun.syndication.io.SyndFeedOutput; -import com.wimdupont.personalweb.service.RSSFeedGenerator; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Writer; -import java.util.Date; - -import static com.google.common.collect.Lists.newArrayList; - -@Service -@Transactional -@RequiredArgsConstructor -public class RSSFeedGeneratorImpl implements RSSFeedGenerator { - private static final String FEED_TYPE = "rss_2.0"; - private static final String FEED_NAME = "feed.xml"; - - @Override - public void generate() throws IOException, FeedException { - SyndFeed feed = new SyndFeedImpl(); - feed.setFeedType(FEED_TYPE); - - feed.setTitle("title"); - feed.setLink("www.wimdupont.com/rss"); - feed.setDescription("World's greatest RSS feed"); - - SyndEntry entry = new SyndEntryImpl(); - SyndContent content = new SyndContentImpl(); - - entry.setTitle("Wow"); - entry.setLink("www.wimdupont.com/blog"); - entry.setPublishedDate(new Date()); - - content.setType("text/plain"); - content.setValue("Content Value"); - entry.setDescription(content); - feed.setEntries(newArrayList(entry)); - - Writer writer = new FileWriter(FEED_NAME); - SyndFeedOutput output = new SyndFeedOutput(); - output.output(feed, writer); - writer.close(); - } -} diff --git a/src/main/java/com/wimdupont/personalweb/service/impl/RssFeedImpl.java b/src/main/java/com/wimdupont/personalweb/service/impl/RssFeedImpl.java @@ -1,52 +0,0 @@ -package com.wimdupont.personalweb.service.impl; - -import com.rometools.rome.feed.rss.Channel; -import com.rometools.rome.feed.rss.Description; -import com.rometools.rome.feed.rss.Guid; -import com.rometools.rome.feed.rss.Item; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import javax.transaction.Transactional; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; - -@Service -@Transactional -@RequiredArgsConstructor -public class RssFeedImpl { - - public Channel rss() { - Channel channel = new Channel(); - channel.setFeedType("rss_2.0"); - channel.setTitle("Wim Dupont"); - channel.setDescription("Updates from Wim Dupont"); - channel.setLink("https://wimdupont.com/rss.xml"); - channel.setUri("https://wimdupont.com/rss.xml"); - channel.setGenerator("Wim's Generatorâ„¢"); - - Date postDate = new GregorianCalendar(2021, Calendar.MAY, 23).getTime(); - channel.setPubDate(postDate); - - Item item = new Item(); - item.setAuthor("Wim Dupont"); - item.setLink("https://wimdupont.com/blog"); - item.setTitle("New blog feed!"); - item.setUri("https://wimdupont.com/blog"); - Guid guid = new Guid(); - guid.setValue("https://wimdupont.com/blog/firstpost"); - item.setGuid(guid); - - Description descr = new Description(); - descr.setValue( - "New blogging space!</br>" - + "The blog itself will be visible on <a rel=\"nofollow\" href=\"https://wimdupont.com/blog/>"); - item.setDescription(descr); - item.setPubDate(postDate); - - channel.setItems(Collections.singletonList(item)); - return channel; - } -} diff --git a/src/main/java/com/wimdupont/personalweb/util/Constants.java b/src/main/java/com/wimdupont/personalweb/util/Constants.java @@ -0,0 +1,10 @@ +package com.wimdupont.personalweb.util; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class Constants { + + public static final String ARTICLES_DIRECTORY = System.getProperty("user.home") + "/personalweb/articles"; +} diff --git a/src/main/resources/adoc/article1.adoc b/src/main/resources/adoc/article1.adoc @@ -1,21 +0,0 @@ -== Article1 - -* This is Work -* in -* progress - -.Some Java code -[source,java] ----- -public class Main{ - public static void main(String... args){ - System.out.println("Hello World!"); - } -} ----- - -Back to normal text. - -=== h3 title - -text -\ No newline at end of file diff --git a/src/main/resources/adoc/article2.adoc b/src/main/resources/adoc/article2.adoc @@ -1,4 +0,0 @@ -== Article 2 - -. line1 -. anoter line -\ No newline at end of file diff --git a/src/main/resources/templates/article.html b/src/main/resources/templates/article.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="en" xmlns:th="http://www.thymeleaf.org"> +<head> + <meta charset="UTF-8"> + <title th:text="${title}"></title> + <link href="/css/main.css" rel="stylesheet"> + <link href="/css/adoc.css" rel="stylesheet"> +</head> +<body> +<div class="container wrapper"> + <header> + <h1 th:utext="${title}"/> + <div th:insert="navigation"/> + </header> + <div th:if="${article != null}"> + <div class="adoc" th:utext="${article}"/> + </div> +</div> +</body> +</html> diff --git a/src/main/resources/templates/blog.html b/src/main/resources/templates/blog.html @@ -12,7 +12,9 @@ <h1>Blog</h1> <div th:insert="navigation"/> </header> - <div class="adoc" th:utext="${article1}"/> + <tr th:each="article : ${articles}"> + <p><a th:href="@{/blog/article/{article}(article=${article})}">[[${article}]]</a></p> + </tr> </div> </body> </html>