001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.templating; 019 020import com.hubspot.jinjava.Jinjava; 021import com.hubspot.jinjava.JinjavaConfig; 022import com.hubspot.jinjava.interpret.InterpretException; 023import com.hubspot.jinjava.interpret.RenderResult; 024import com.hubspot.jinjava.interpret.TemplateError; 025import com.hubspot.jinjava.lib.filter.Filter; 026import com.hubspot.jinjava.lib.tag.Tag; 027import com.hubspot.jinjava.loader.ResourceNotFoundException; 028import io.stallion.exceptions.UsageException; 029import io.stallion.exceptions.WebException; 030import io.stallion.services.Log; 031import io.stallion.settings.Settings; 032import org.apache.commons.lang3.StringUtils; 033import org.apache.commons.lang3.exception.ExceptionUtils; 034 035import java.io.IOException; 036import java.nio.charset.Charset; 037import java.util.Iterator; 038import java.util.List; 039import java.util.Map; 040import java.util.stream.Collectors; 041 042import static io.stallion.utils.Literals.*; 043 044public class JinjaTemplating implements Templating { 045 private static String targetFolder = ""; 046 private static Jinjava jinjava; 047 private boolean devMode = false; 048 049 public JinjaTemplating(String folder, boolean devMode) { 050 this.devMode = devMode; 051 init(folder); 052 } 053 054 @Override 055 public void init(String folder) { 056 this.targetFolder = folder; 057 reset(); 058 } 059 060 private static List<Tag> tags; 061 private static List<Filter> filters; 062 063 @Override 064 public void reset() { 065 066 //JinjavaConfig config = new JinjavaConfig(); 067 //config.setResourceLocator(new MyCustomResourceLocator()); 068 069 if (tags == null) { 070 tags = list(); 071 } 072 if (filters == null) { 073 filters = list(new MarkdownFilter()); 074 } 075 076 077 //JinjavaConfig config = JinjavaConfig.newBuilder().withMaxRenderDepth(0).build(); 078 //jinjava = new Jinjava(config); 079 jinjava = new Jinjava(); 080 081 jinjava.setResourceLocator(new JinjaResourceLocator(targetFolder, devMode)); 082 for (Filter filter: filters) { 083 jinjava.getGlobalContext().registerFilter(filter); 084 } 085 for (Tag tag: tags) { 086 jinjava.getGlobalContext().registerTag(tag); 087 } 088 089 JinjaResourceLocator.clearCache(); 090 } 091 092 @Override 093 public Boolean templateExists(String path) { 094 if (path.contains("\n")) { 095 return true; 096 } 097 098 String result = null; 099 try { 100 result = jinjava.getResourceLocator().getString(path, Charset.forName("UTF-8"), null); 101 } catch (ResourceNotFoundException e) { 102 return false; 103 } catch (IOException e) { 104 throw new RuntimeException(e); 105 } 106 107 if (StringUtils.isEmpty(result)) { 108 return false; 109 } else { 110 return true; 111 } 112 } 113 114 @Override 115 public String renderTemplate(String template, Map<String, Object> context) { 116 String content = ""; 117 try { 118 String templateString = ""; 119 if (template.contains("\n")) { 120 templateString = template; 121 } else { 122 templateString = jinjava.getResourceLocator().getString(template, Charset.forName("UTF-8"), null); 123 } 124 if (templateString == null && Settings.instance().isStrict()) { 125 throw new UsageException("Could not find the template: " + template); 126 } else if (templateString == null) { 127 templateString = ""; 128 } 129 RenderResult result = jinjava.renderForResult(templateString, context); 130 List fatalErrors = (List)result.getErrors().stream().filter((error) -> { 131 return error.getSeverity() == TemplateError.ErrorType.FATAL; 132 }).collect(Collectors.toList()); 133 if(!fatalErrors.isEmpty()) { 134 Log.warn("Exception when rendering template: {0}", template); 135 throw new FatalTemplateErrorsException(template, fatalErrors); 136 } else { 137 return result.getOutput(); 138 } 139 } catch (IOException e) { 140 throw new RuntimeException(e); 141 } 142 } 143 144 public static class FatalTemplateErrorsException extends InterpretException { 145 private final String templatePath; 146 private final Iterable<TemplateError> errors; 147 148 public FatalTemplateErrorsException(String templatePath, Iterable<TemplateError> errors) { 149 super("Fatal error rendering template \"" + templatePath + "\".\n" + generateMessage(errors)); 150 this.templatePath = templatePath; 151 this.errors = errors; 152 } 153 154 private static String generateMessage(Iterable<TemplateError> errors) { 155 StringBuilder msg = new StringBuilder(); 156 Iterator var2 = errors.iterator(); 157 158 while(var2.hasNext()) { 159 TemplateError error = (TemplateError)var2.next(); 160 msg.append(error.toString()).append('\n'); 161 if(error.getException() != null) { 162 msg.append(ExceptionUtils.getStackTrace(error.getException())).append('\n'); 163 } 164 } 165 166 return msg.toString(); 167 } 168 169 public String getTemplatePath() { 170 return this.templatePath; 171 } 172 173 public Iterable<TemplateError> getErrors() { 174 return this.errors; 175 } 176 } 177 178 179 180 public void registerTag(Tag tag) { 181 tags.add(tag); 182 jinjava.getGlobalContext().registerTag(tag); 183 184 } 185 186 public void registerFilter(Filter filter) { 187 filters.add(filter); 188 jinjava.getGlobalContext().registerFilter(filter); 189 190 } 191 192}