1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.freemarker;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.context.WebContext;
39 import info.magnolia.i18nsystem.TranslationService;
40 import info.magnolia.init.MagnoliaConfigurationProperties;
41 import info.magnolia.objectfactory.Components;
42
43 import java.io.IOException;
44 import java.io.Reader;
45 import java.io.Writer;
46 import java.util.Locale;
47 import java.util.Map;
48 import java.util.Set;
49
50 import javax.inject.Inject;
51 import javax.inject.Singleton;
52 import javax.servlet.GenericServlet;
53 import javax.servlet.ServletContext;
54 import javax.servlet.ServletException;
55
56 import freemarker.cache.TemplateLoader;
57 import freemarker.ext.jsp.TaglibFactory;
58 import freemarker.ext.servlet.FreemarkerServlet;
59 import freemarker.ext.servlet.HttpRequestHashModel;
60 import freemarker.ext.servlet.ServletContextHashModel;
61 import freemarker.template.Configuration;
62 import freemarker.template.ObjectWrapper;
63 import freemarker.template.Template;
64 import freemarker.template.TemplateException;
65 import freemarker.template.TemplateModel;
66 import freemarker.template.TemplateModelException;
67
68
69
70
71
72
73 @Singleton
74 public class FreemarkerHelper {
75
76
77
78
79 @Deprecated
80 public static FreemarkerHelper getInstance() {
81 return Components.getSingleton(FreemarkerHelper.class);
82 }
83
84 private final Configuration cfg;
85 private final TranslationService translationService;
86
87
88 private TaglibFactory taglibFactory;
89 private ServletContextHashModel servletContextHashModel;
90
91 @Inject
92 public FreemarkerHelper(final FreemarkerConfig freemarkerConfig, TranslationService translationService, MagnoliaConfigurationProperties properties) {
93 boolean devMode = properties.getBooleanProperty("magnolia.develop");
94 this.translationService = translationService;
95
96
97 this.cfg = new Configuration() {
98 @Override
99 public Set getSharedVariableNames() {
100 final Set names = super.getSharedVariableNames();
101 names.addAll(freemarkerConfig.getSharedVariables().keySet());
102 return names;
103 }
104
105 @Override
106 public TemplateModel getSharedVariable(String name) {
107 final TemplateModel value = super.getSharedVariable(name);
108 if (value == null) {
109 return freemarkerConfig.getSharedVariables().get(name);
110 }
111 return value;
112 }
113 };
114 cfg.setTemplateExceptionHandler(freemarkerConfig.getTemplateExceptionHandler());
115
116
117
118
119 cfg.setTemplateLoader(new ConfigDelegatingTemplateLoader(freemarkerConfig));
120 cfg.setObjectWrapper(new ConfigDelegatingObjectWrapper(freemarkerConfig));
121
122 cfg.setTagSyntax(Configuration.AUTO_DETECT_TAG_SYNTAX);
123 cfg.setDefaultEncoding("UTF-8");
124 cfg.setURLEscapingCharset("UTF-8");
125 if (devMode) {
126 cfg.setTemplateUpdateDelay(0);
127 }
128 }
129
130
131
132
133 @Deprecated
134 public FreemarkerHelper(final FreemarkerConfig freemarkerConfig, TranslationService transactionService) {
135 this(freemarkerConfig, transactionService, Components.getComponent(MagnoliaConfigurationProperties.class));
136 }
137
138
139
140
141 @Deprecated
142 public FreemarkerHelper(final FreemarkerConfig freemarkerConfig) {
143 this(freemarkerConfig, Components.getComponent(TranslationService.class), Components.getComponent(MagnoliaConfigurationProperties.class));
144 }
145
146
147
148
149 public void render(String templatePath, Object root, Writer out) throws TemplateException, IOException {
150 render(templatePath, null, null, root, out);
151 }
152
153
154
155
156
157
158
159
160
161
162
163 public void render(String templatePath, Locale locale, String i18nBasename, Object root, Writer out) throws TemplateException, IOException {
164 final Locale localeToUse = checkLocale(locale);
165 prepareRendering(localeToUse, i18nBasename, root);
166
167 final Template template = cfg.getTemplate(templatePath, localeToUse);
168 template.process(root, out);
169 }
170
171
172
173
174
175
176
177 public void render(Reader template, Object root, Writer out) throws TemplateException, IOException {
178 render(template, null, null, root, out);
179 }
180
181 protected void render(Reader template, Locale locale, String i18nBasename, Object root, Writer out) throws TemplateException, IOException {
182 final Locale localeToUse = checkLocale(locale);
183 prepareRendering(localeToUse, i18nBasename, root);
184
185 final Template t = new Template("inlinetemplate", template, cfg);
186 t.setLocale(localeToUse);
187 t.process(root, out);
188 }
189
190
191
192
193 protected Locale checkLocale(Locale locale) {
194 if (locale != null) {
195 return locale;
196 } else if (MgnlContext.hasInstance()) {
197 return MgnlContext.getLocale();
198 } else {
199 return Locale.getDefault();
200 }
201 }
202
203
204
205
206 protected void prepareRendering(Locale checkedLocale, String i18nBasename, Object root) {
207 if (root instanceof Map) {
208 final Map<String, Object> data = (Map<String, Object>) root;
209 addDefaultData(data, checkedLocale, i18nBasename);
210 }
211 }
212
213 protected void addDefaultData(Map<String, Object> data, Locale locale, String i18nBasename) {
214 if (MgnlContext.hasInstance()) {
215 data.put("ctx", MgnlContext.getInstance());
216 }
217 if (MgnlContext.isWebContext()) {
218 final WebContext webCtx = MgnlContext.getWebContext();
219
220 data.put("contextPath", webCtx.getContextPath());
221 data.put("aggregationState", webCtx.getAggregationState());
222
223 addTaglibSupportData(data, webCtx);
224 }
225
226 data.put("defaultBaseUrl", ServerConfiguration.getInstance().getDefaultBaseUrl());
227
228 data.put("i18n", new MessagesWrapper(i18nBasename, locale, translationService));
229 data.put("i18nAuthoring", new MessagesWrapper(i18nBasename, MgnlContext.getLocale(), translationService));
230
231
232
233
234
235
236
237 }
238
239 protected void addTaglibSupportData(Map<String, Object> data, WebContext webCtx) {
240 final ServletContext servletContext = webCtx.getServletContext();
241 try {
242 data.put(FreemarkerServlet.KEY_JSP_TAGLIBS, checkTaglibFactory(servletContext));
243 data.put(FreemarkerServlet.KEY_APPLICATION_PRIVATE, checkServletContextModel(servletContext));
244 data.put(FreemarkerServlet.KEY_REQUEST_PRIVATE, new HttpRequestHashModel(webCtx.getRequest(), webCtx.getResponse(), cfg.getObjectWrapper()));
245 } catch (ServletException e) {
246
247 throw new RuntimeException("Can't initialize taglib support for FreeMarker: ", e);
248 }
249 }
250
251 protected TaglibFactory checkTaglibFactory(ServletContext servletContext) {
252 if (taglibFactory == null) {
253 taglibFactory = new TaglibFactory(new FreemarkerServletContextWrapper(servletContext));
254 }
255 return taglibFactory;
256 }
257
258 protected ServletContextHashModel checkServletContextModel(ServletContext servletContext) throws ServletException {
259 if (servletContextHashModel == null) {
260
261 final GenericServlet fs = new DoNothingServlet(servletContext);
262 servletContextHashModel = new ServletContextHashModel(fs, cfg.getObjectWrapper());
263 }
264 return servletContextHashModel;
265 }
266
267 protected Configuration getConfiguration() {
268 return cfg;
269 }
270
271 private static class ConfigDelegatingTemplateLoader implements TemplateLoader {
272 private final FreemarkerConfig freemarkerConfig;
273
274 public ConfigDelegatingTemplateLoader(FreemarkerConfig freemarkerConfig) {
275 this.freemarkerConfig = freemarkerConfig;
276 }
277
278 @Override
279 public Object findTemplateSource(String name) throws IOException {
280 return freemarkerConfig.getTemplateLoader().findTemplateSource(name);
281 }
282
283 @Override
284 public long getLastModified(Object templateSource) {
285 return freemarkerConfig.getTemplateLoader().getLastModified(templateSource);
286 }
287
288 @Override
289 public Reader getReader(Object templateSource, String encoding) throws IOException {
290 return freemarkerConfig.getTemplateLoader().getReader(templateSource, encoding);
291 }
292
293 @Override
294 public void closeTemplateSource(Object templateSource) throws IOException {
295 freemarkerConfig.getTemplateLoader().closeTemplateSource(templateSource);
296 }
297 }
298
299 private static class ConfigDelegatingObjectWrapper implements ObjectWrapper {
300 private final FreemarkerConfig freemarkerConfig;
301
302 public ConfigDelegatingObjectWrapper(FreemarkerConfig freemarkerConfig) {
303 this.freemarkerConfig = freemarkerConfig;
304 }
305
306 @Override
307 public TemplateModel wrap(Object obj) throws TemplateModelException {
308 return freemarkerConfig.getObjectWrapper().wrap(obj);
309 }
310 }
311 }