blog-software-java/src/main/java/eu/m724/blog/template/TemplateRenderer.java
Minecon724 c57ae37666
All checks were successful
/ build (push) Successful in 1m3s
Page templates
Closes #8

Signed-off-by: Minecon724 <git@m724.eu>
2025-03-12 19:26:18 +01:00

147 lines
5.1 KiB
Java

/*
* Copyright (c) 2025 blog-software-java developers
* blog-software-java is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.blog.template;
import eu.m724.blog.Minifier;
import eu.m724.blog.object.Article;
import eu.m724.blog.object.PageNumbers;
import eu.m724.blog.object.Site;
import io.pebbletemplates.pebble.PebbleEngine;
import io.pebbletemplates.pebble.error.LoaderException;
import io.pebbletemplates.pebble.loader.FileLoader;
import io.pebbletemplates.pebble.template.PebbleTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
/**
* The {@code TemplateRenderer} class is responsible for rendering dynamic HTML templates
* using the Pebble templating engine.
*/
public class TemplateRenderer {
private static final Logger LOGGER = LoggerFactory.getLogger(TemplateRenderer.class);
private final Site site;
private final Minifier minifier;
private final PebbleEngine pebbleEngine;
private final PebbleTemplate indexTemplate, articleTemplate, pageTemplate;
/**
* Constructs a TemplateRenderer instance for rendering templates from the specified directory.
* @param site the {@link Site} this renderer renders
* @param templateDirectory the root directory containing the template file
* @param fileHashes file hashes. currently only applies to assets
*/
public TemplateRenderer(Site site, Minifier minifier, Path templateDirectory, Map<String, String> fileHashes) {
this.site = site;
this.minifier = minifier;
var loader = new FileLoader();
loader.setPrefix(templateDirectory.toString());
loader.setSuffix(".html");
this.pebbleEngine = new PebbleEngine.Builder()
.loader(loader)
.extension(new TemplateExtension(site, fileHashes))
.build();
this.indexTemplate = pebbleEngine.getTemplate("index_template");
this.articleTemplate = pebbleEngine.getTemplate("article_template");
var pageTemplate = indexTemplate;
try {
pageTemplate = pebbleEngine.getTemplate("page_template");
} catch (LoaderException e) {
LOGGER.debug("Template has no page template, using index_template instead");
}
this.pageTemplate = pageTemplate;
}
/**
* Renders a page using this template.<br>
* This is different from index, as this uses page template.
*
* @param pageArticles the {@link Article}s to be included in the <strong>CURRENT</strong> page
* @param pageNumbers a {@link PageNumbers} instance for the current page
* @return the rendered index HTML page as a string
* @throws IOException if an error occurs during the template evaluation
*/
public String renderPage(PageNumbers pageNumbers, List<Article> pageArticles) throws IOException {
var context = Map.of(
"page", pageNumbers,
"articles", pageArticles
);
return renderTemplate(pageTemplate, context);
}
/**
* Renders the index page using this template.<br>
* This is different from rendering page 1, as this uses index template.
*
* @param pageArticles the {@link Article}s to be included in the <strong>CURRENT</strong> page
* @param pageNumbers a {@link PageNumbers} instance for the current page
* @return the rendered index HTML page as a string
* @throws IOException if an error occurs during the template evaluation
*/
public String renderIndex(PageNumbers pageNumbers, List<Article> pageArticles) throws IOException {
var context = Map.of(
"page", pageNumbers,
"articles", pageArticles
);
return renderTemplate(indexTemplate, context);
}
/**
* Renders the content of an article using this template.
*
* @param article the {@link Article} to be rendered
* @return the rendered article HTML page as a string
* @throws IOException if an error occurs during template evaluation
*/
public String renderArticle(Article article) throws IOException {
String content = article.rawContent();
if (site.templateArticles()) {
var context = Map.of(
"article", article
);
content = renderTemplate(pebbleEngine.getLiteralTemplate(content), context);
}
var context = Map.of(
"article", article,
"content", content
);
return renderTemplate(articleTemplate, context);
}
private String renderTemplate(PebbleTemplate template, Map<String, ?> context) throws IOException {
var writer = new StringWriter();
template.evaluate(writer, (Map<String, Object>) context);
var html = writer.toString();
if (minifier != null) { // not checking site.minify because the minifier may not be available
html = minifier.minify(html);
}
return html;
}
}