diff --git a/src/main/java/me/mrletsplay/mdblog/MdBlog.java b/src/main/java/me/mrletsplay/mdblog/MdBlog.java index 00dd02d..f30ce63 100644 --- a/src/main/java/me/mrletsplay/mdblog/MdBlog.java +++ b/src/main/java/me/mrletsplay/mdblog/MdBlog.java @@ -7,11 +7,11 @@ import java.nio.file.StandardWatchEventKinds; import java.nio.file.WatchKey; import java.nio.file.WatchService; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -19,6 +19,8 @@ import me.mrletsplay.mdblog.blog.Post; import me.mrletsplay.mdblog.blog.PostMetadata; import me.mrletsplay.mdblog.markdown.MdParser; import me.mrletsplay.mdblog.markdown.MdRenderer; +import me.mrletsplay.mdblog.template.Template; +import me.mrletsplay.mdblog.template.Templates; import me.mrletsplay.mdblog.util.PostPath; import me.mrletsplay.mdblog.util.TimeFormatter; import me.mrletsplay.mrcore.http.HttpUtils; @@ -35,22 +37,14 @@ public class MdBlog { FILES_PATH = Path.of("files"), POSTS_PATH = FILES_PATH.resolve("posts"); - private static final String - INDEX_NAME = "index", - INDEX_POST_NAME = "index-post", - INDEX_SUB_BLOG_NAME = "index-sub-blog"; - private static HttpServer server; private static WatchService watchService; private static List watchedDirectories; private static Map posts; - private static Map indexTemplates; + private static Map indexTemplates; private static Map globalResources; - private static String - defaultIndexTemplate, - defaultIndexSubBlogTemplate, - defaultIndexPostTemplate; + private static Templates defaultTemplates; public static void main(String[] args) throws IOException { server = new HttpServer(HttpServer.newConfigurationBuilder() @@ -61,9 +55,10 @@ public class MdBlog { server.getDocumentProvider().register(HttpRequestMethod.GET, "/", () -> createPostsIndex(PostPath.root())); server.getDocumentProvider().registerPattern(HttpRequestMethod.GET, "/{path...}", () -> handleRequest(HttpRequestContext.getCurrentContext())); - defaultIndexTemplate = Files.readString(extract("template/index.md")); - defaultIndexPostTemplate = Files.readString(extract("template/index-post.md")); - defaultIndexSubBlogTemplate = Files.readString(extract("template/index-sub-blog.md")); + defaultTemplates = new Templates(null); + for(Template template : Template.values()) { + defaultTemplates.put(template, Files.readString(extract("template/" + template.getName() + ".md"))); + } server.start(); @@ -123,9 +118,7 @@ public class MdBlog { String blogName = path.getName(); - String indexTemplate = indexTemplates.getOrDefault(path.concat(PostPath.parse(INDEX_NAME)), defaultIndexTemplate); - String indexSubBlogTemplate = indexTemplates.getOrDefault(path.concat(PostPath.parse(INDEX_SUB_BLOG_NAME)), defaultIndexSubBlogTemplate); - String indexPostTemplate = indexTemplates.getOrDefault(path.concat(PostPath.parse(INDEX_POST_NAME)), defaultIndexPostTemplate); + Templates templates = indexTemplates.getOrDefault(path, defaultTemplates); HtmlDocument index = new HtmlDocument(); index.setTitle("Index of " + blogName); @@ -133,49 +126,44 @@ public class MdBlog { index.addStyleSheet("_/style/base.css"); index.addStyleSheet("_/style/index.css"); - String indexMd = indexTemplate; - indexMd = indexMd.replace("{name}", blogName); - indexMd = indexMd.replace("{sub_blogs}", directories.stream() - .map(p -> { - String subBlogMd = indexSubBlogTemplate; + String indexMd = templates.render(Template.INDEX, + "name", blogName, + "sub_blogs", directories.stream() + .map(p -> { + HtmlElement name = new HtmlElement("a"); + name.setAttribute("href", p.toString()); + name.setText(p.getName()); + return templates.render(Template.INDEX_SUB_BLOG, + "name", name.toString()); + }) + .collect(Collectors.joining("\n\n")), + "posts", postsInDir.stream() + .map(p -> { + Post post = posts.get(path == null ? p : path.concat(p)); + PostMetadata meta = post.getMetadata(); + if(tag != null && !meta.tags().contains(tag)) return null; - HtmlElement name = new HtmlElement("a"); - name.setAttribute("href", p.toString()); - name.setText(p.getName()); - subBlogMd = subBlogMd.replace("{name}", name.toString()); - return subBlogMd; - }) - .collect(Collectors.joining("\n\n"))); + HtmlElement title = new HtmlElement("a"); + title.setAttribute("href", p.toString()); + title.setText(meta.title()); - indexMd = indexMd.replace("{posts}", postsInDir.stream() - .map(p -> { - String postMd = indexPostTemplate; - Post post = posts.get(path == null ? p : path.concat(p)); - PostMetadata meta = post.getMetadata(); - if(tag != null && !meta.tags().contains(tag)) return null; - - HtmlElement title = new HtmlElement("a"); - title.setAttribute("href", p.toString()); - title.setText(meta.title()); - postMd = postMd.replace("{title}", title.toString()); - - postMd = postMd.replace("{author}", meta.author()); - postMd = postMd.replace("{date}", TimeFormatter.toDateOnly(meta.date())); - postMd = postMd.replace("{date_time}", TimeFormatter.toDateAndTime(meta.date())); - postMd = postMd.replace("{date_relative}", TimeFormatter.toRelativeTime(meta.date())); - postMd = postMd.replace("{tags}", meta.tags().stream() - .map(t -> { - HtmlElement link = new HtmlElement("a"); - link.setText(t); - link.setAttribute("href", "?tag=" + HttpUtils.urlEncode(t)); - return link.toString(); - }) - .collect(Collectors.joining(", "))); - postMd = postMd.replace("{description}", meta.description()); - return postMd; - }) - .filter(Objects::nonNull) - .collect(Collectors.joining("\n\n"))); + return templates.render(Template.INDEX_POST, + "title", title.toString(), + "author", meta.author(), + "date", TimeFormatter.toDateOnly(meta.date()), + "date_time", TimeFormatter.toDateAndTime(meta.date()), + "date_relative", TimeFormatter.toRelativeTime(meta.date()), + "description", meta.description(), + "tags", meta.tags().stream() + .map(t -> { + HtmlElement link = new HtmlElement("a"); + link.setText(t); + link.setAttribute("href", "?tag=" + HttpUtils.urlEncode(t)); + return link.toString(); + }) + .collect(Collectors.joining(", "))); + }) + .collect(Collectors.joining("\n\n"))); index.getBodyNode().appendChild(new MdRenderer().render(MdParser.parse(indexMd))); index.createContent(); @@ -200,6 +188,10 @@ public class MdBlog { Post post = posts.get(path); if(post != null) { + if(rawPath.endsWith("/")) { + ctx.redirect("../" + path.subPath(path.length() - 1)); + } + post.getContent().createContent(); return; } @@ -237,6 +229,7 @@ public class MdBlog { private static Path extract(String path) throws IOException { Path filePath = FILES_PATH.resolve(path); if(!Files.exists(filePath)) { + System.out.println("Extracting " + path); Files.createDirectories(filePath.getParent()); Files.write(filePath, MdBlog.class.getResourceAsStream("/" + path).readAllBytes()); } @@ -248,33 +241,43 @@ public class MdBlog { } private static void updateBlogs() throws IOException { - Iterator it = posts.values().iterator(); - while(it.hasNext()) { - Post p = it.next(); - if(!p.update()) it.remove(); - } - + System.out.println("Update"); indexTemplates.clear(); + System.out.println(posts); + Files.walk(POSTS_PATH) .filter(Files::isRegularFile) .filter(f -> f.getFileName().toString().endsWith(Post.FILE_EXTENSION)) .filter(f -> posts.values().stream().noneMatch(p -> p.getFilePath().equals(f))) .forEach(f -> { try { - String postName = f.getFileName().toString(); - postName = postName.substring(0, postName.length() - Post.FILE_EXTENSION.length()); + String fullPostName = f.getFileName().toString(); + String postName = fullPostName.substring(0, fullPostName.length() - Post.FILE_EXTENSION.length()); PostPath path = PostPath.of(POSTS_PATH.relativize(f).getParent(), postName); - if(INDEX_NAME.equals(postName) || INDEX_SUB_BLOG_NAME.equals(postName) || INDEX_POST_NAME.equals(postName)) { + Template template = Arrays.stream(Template.values()) + .filter(t -> t.getName().equals(postName)) + .findFirst().orElse(null); + if(template != null) { // File is an index template, don't parse it as a post - indexTemplates.put(path, Files.readString(f, StandardCharsets.UTF_8)); + indexTemplates.computeIfAbsent(path.getParent(), p -> new Templates(defaultTemplates)).put(template, Files.readString(f, StandardCharsets.UTF_8)); return; } posts.put(path, new Post(f)); } catch (IOException e) {} }); + + Iterator> it = posts.entrySet().iterator(); + while(it.hasNext()) { + Map.Entry en = it.next(); + PostPath path = en.getKey(); + Post p = en.getValue(); + + Templates templates = indexTemplates.getOrDefault(path.getParent(), defaultTemplates); + if(!p.update(templates)) it.remove(); + } } private static void watchFolders() throws IOException { diff --git a/src/main/java/me/mrletsplay/mdblog/blog/Post.java b/src/main/java/me/mrletsplay/mdblog/blog/Post.java index 97675a6..3e0036a 100644 --- a/src/main/java/me/mrletsplay/mdblog/blog/Post.java +++ b/src/main/java/me/mrletsplay/mdblog/blog/Post.java @@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException; import me.mrletsplay.mdblog.markdown.MdParser; import me.mrletsplay.mdblog.markdown.MdRenderer; -import me.mrletsplay.mrcore.misc.ByteUtils; +import me.mrletsplay.mdblog.template.Template; +import me.mrletsplay.mdblog.template.Templates; +import me.mrletsplay.mdblog.util.TimeFormatter; import me.mrletsplay.simplehttpserver.dom.html.HtmlDocument; public class Post { @@ -27,14 +29,12 @@ public class Post { } private Path filePath; - private String checksum; +// private String checksum; TODO currently unused, post also needs to update when templates change private PostMetadata metadata; private HtmlDocument content; public Post(Path filePath) throws IOException { this.filePath = filePath; - this.checksum = checksum(filePath); - load(); } public Path getFilePath() { @@ -54,7 +54,7 @@ public class Post { return content; } - private void load() throws IOException { + private void load(Templates templates) throws IOException { String postData = Files.readString(filePath); String[] spl = postData.split("\n---\n", 2); if(spl.length != 2) throw new IOException("Invalid post file"); @@ -62,30 +62,37 @@ public class Post { this.metadata = PostMetadata.load(spl[0]); HtmlDocument document = new HtmlDocument(); - document.getBodyNode().appendChild(RENDERER.render(MdParser.parse(spl[1]))); + document.getBodyNode().appendChild(RENDERER.render(MdParser.parse(templates.render(Template.POST, + "content", spl[1], + "title", metadata.title(), + "author", metadata.author(), + "description", metadata.description(), + "date", TimeFormatter.toDateOnly(metadata.date()), + "date_time", TimeFormatter.toDateAndTime(metadata.date()), + "date_relative", TimeFormatter.toRelativeTime(metadata.date()))))); document.setTitle(metadata.title()); document.setDescription(metadata.description()); - document.addStyleSheet("/_/style/base.css"); - document.addStyleSheet("/_/style/post.css"); + document.addStyleSheet("_/style/base.css"); + document.addStyleSheet("_/style/post.css"); this.content = document; } - public boolean update() { + public boolean update(Templates templates) { if(!Files.exists(filePath)) return false; try { - String newChecksum = checksum(filePath); - if(checksum.equals(newChecksum)) return true; - this.checksum = newChecksum; - load(); + //String newChecksum = checksum(filePath); + //if(checksum != null && checksum.equals(newChecksum)) return true; + //this.checksum = newChecksum; + load(templates); return true; } catch (IOException e) { return false; } } - private static String checksum(Path filePath) throws IOException { - return ByteUtils.bytesToHex(MD_5.digest(Files.readAllBytes(filePath))); - } +// private static String checksum(Path filePath) throws IOException { +// return ByteUtils.bytesToHex(MD_5.digest(Files.readAllBytes(filePath))); +// } } diff --git a/src/main/java/me/mrletsplay/mdblog/template/Template.java b/src/main/java/me/mrletsplay/mdblog/template/Template.java new file mode 100644 index 0000000..d5c8170 --- /dev/null +++ b/src/main/java/me/mrletsplay/mdblog/template/Template.java @@ -0,0 +1,21 @@ +package me.mrletsplay.mdblog.template; + +public enum Template { + + INDEX("index"), + INDEX_POST("index-post"), + INDEX_SUB_BLOG("index-sub-blog"), + POST("post"), + ; + + private final String name; + + private Template(String name) { + this.name = name; + } + + public String getName() { + return name; + } + +} diff --git a/src/main/java/me/mrletsplay/mdblog/template/Templates.java b/src/main/java/me/mrletsplay/mdblog/template/Templates.java new file mode 100644 index 0000000..cb14932 --- /dev/null +++ b/src/main/java/me/mrletsplay/mdblog/template/Templates.java @@ -0,0 +1,37 @@ +package me.mrletsplay.mdblog.template; + +import java.util.HashMap; +import java.util.Map; + +public class Templates { + + private Templates defaults; + private Map templates; + + public Templates(Templates defaults) { + this.defaults = defaults; + this.templates = new HashMap<>(); + } + + public String get(Template template) { + return templates.getOrDefault(template, defaults == null ? null : defaults.get(template)); + } + + public void put(Template template, String templateContent) { + templates.put(template, templateContent); + } + + public String render(Template template, String... variables) { + if(variables.length % 2 != 0) throw new IllegalArgumentException("Invalid number of arguments"); + + String content = get(template); + + for(int i = 0; i < variables.length; i += 2) { + String variable = variables[i]; + content = content.replace("{" + variable + "}", variables[i + 1]); + } + + return content; + } + +} diff --git a/src/main/resources/template/post.md b/src/main/resources/template/post.md new file mode 100644 index 0000000..50b69da --- /dev/null +++ b/src/main/resources/template/post.md @@ -0,0 +1,6 @@ +# {title} +by {author} {date_relative} + +{description} + +{content} \ No newline at end of file