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.rendering.renderer;
35
36 import info.magnolia.cms.core.AggregationState;
37 import info.magnolia.context.MgnlContext;
38 import info.magnolia.jcr.decoration.ContentDecoratorUtil;
39 import info.magnolia.jcr.util.ContentMap;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.jcr.wrapper.ChannelVisibilityContentDecorator;
42 import info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper;
43 import info.magnolia.jcr.wrapper.I18nNodeWrapper;
44 import info.magnolia.objectfactory.Components;
45 import info.magnolia.objectfactory.MgnlInstantiationException;
46 import info.magnolia.objectfactory.ParameterInfo;
47 import info.magnolia.objectfactory.ParameterResolver;
48 import info.magnolia.rendering.context.RenderingContext;
49 import info.magnolia.rendering.engine.RenderException;
50 import info.magnolia.rendering.model.EarlyExecutionAware;
51 import info.magnolia.rendering.model.ModelExecutionFilter;
52 import info.magnolia.rendering.model.RenderingModel;
53 import info.magnolia.rendering.model.RenderingModelImpl;
54 import info.magnolia.rendering.template.RenderableDefinition;
55
56 import java.lang.reflect.InvocationTargetException;
57 import java.util.HashMap;
58 import java.util.Map;
59 import java.util.Map.Entry;
60
61 import javax.jcr.Node;
62 import javax.jcr.RepositoryException;
63
64 import org.apache.commons.beanutils.BeanUtils;
65 import org.apache.commons.lang.StringUtils;
66 import org.apache.commons.lang.exception.ExceptionUtils;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public abstract class AbstractRenderer implements Renderer, RenderingModelBasedRenderer {
83
84 protected static final String MODEL_ATTRIBUTE = RenderingModel.class.getName();
85
86 private Map<String, ContextAttributeConfiguration> contextAttributes = new HashMap<String, ContextAttributeConfiguration>();
87
88 @Override
89 public void render(RenderingContext renderingCtx, Map<String, Object> contextObjects) throws RenderException {
90
91 final RenderingModel<?> parentModel = MgnlContext.getAttribute(MODEL_ATTRIBUTE);
92 Node content = renderingCtx.getCurrentContent();
93 RenderableDefinition definition = renderingCtx.getRenderableDefinition();
94
95 RenderingModel<?> model = null;
96 String actionResult = null;
97
98 if (content != null) {
99 String uuid;
100 try {
101 uuid = content.getIdentifier();
102 }
103 catch (RepositoryException e) {
104 throw new RenderException(e);
105 }
106
107 model = MgnlContext.getAttribute(ModelExecutionFilter.MODEL_ATTRIBUTE_PREFIX + uuid);
108 if (model != null) {
109 actionResult = (String) MgnlContext.getAttribute(ModelExecutionFilter.ACTION_RESULT_ATTRIBUTE_PREFIX + uuid);
110 if (model instanceof EarlyExecutionAware) {
111 ((EarlyExecutionAware)model).setParent(parentModel);
112 }
113 }
114 }
115
116 if (model == null) {
117 model = newModel(content, definition, parentModel);
118 if (model != null) {
119 actionResult = model.execute();
120 if (RenderingModel.SKIP_RENDERING.equals(actionResult)) {
121 return;
122 }
123 }
124 }
125
126 String templatePath = resolveTemplateScript(content, definition, model, actionResult);
127 if(templatePath == null){
128 throw new RenderException("No template script defined for the template definition [" + definition + "]");
129 }
130
131 final Map<String, Object> ctx = newContext();
132 final Map<String, Object> savedContextState = saveContextState(ctx);
133 setupContext(ctx, content, definition, model, actionResult);
134 ctx.putAll(contextObjects);
135 MgnlContext.setAttribute(MODEL_ATTRIBUTE, model);
136 content = wrapNodeForModel(content);
137 renderingCtx.push(content, definition);
138 try {
139 onRender(content, definition, renderingCtx, ctx, templatePath);
140 } finally {
141 renderingCtx.pop();
142 }
143 MgnlContext.setAttribute(MODEL_ATTRIBUTE, parentModel);
144
145 restoreContext(ctx, savedContextState);
146 }
147
148
149
150
151
152
153
154
155 protected String resolveTemplateScript(Node content, RenderableDefinition definition, RenderingModel<?> model, final String actionResult) {
156 return definition.getTemplateScript();
157 }
158
159
160
161
162
163
164 @Override
165 public RenderingModel<?> newModel(final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
166
167 Class clazz = definition.getModelClass();
168
169
170 if (clazz == null) {
171 clazz = RenderingModelImpl.class;
172 }
173
174 final Node wrappedContent = wrapNodeForModel(content);
175
176 return newModel(clazz, wrappedContent, definition, parentModel);
177 }
178
179 protected <T extends RenderingModel<?>> T newModel(Class<T> modelClass, final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
180
181 try {
182
183 T model = Components.getComponentProvider().newInstanceWithParameterResolvers(modelClass,
184 new ParameterResolver() {
185 @Override
186 public Object resolveParameter(ParameterInfo parameter) {
187 if (parameter.getParameterType().equals(Node.class)) {
188 return content;
189 }
190 if (parameter.getParameterType().isAssignableFrom(definition.getClass())) {
191 return definition;
192 }
193 if (parameter.getParameterType().equals(RenderingModel.class)) {
194 return parentModel;
195 }
196 return UNRESOLVED;
197 }
198 }
199 );
200
201
202 Map<String, String[]> params = MgnlContext.getWebContext().getRequest().getParameterMap();
203
204
205 Map<String, Object> filtered = new HashMap<String, Object>();
206 if (params != null) {
207 for (Entry<String, String[]> entry : params.entrySet()) {
208 String key = entry.getKey();
209 String[] value = entry.getValue();
210 if (StringUtils.contains(key, "[")) {
211 key = StringUtils.substringBefore(key, "[");
212 }
213 filtered.put(key, value);
214 }
215 }
216
217 BeanUtils.populate(model, filtered);
218
219 return model;
220
221 } catch (MgnlInstantiationException e) {
222 throw new RenderException("Can't instantiate model: " + modelClass, e);
223 } catch (InvocationTargetException e) {
224 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
225 } catch (IllegalAccessException e) {
226 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
227 }
228 }
229
230 protected Map<String, Object> saveContextState(final Map<String, Object> ctx) {
231 Map<String, Object> state = new HashMap<String, Object>();
232
233 saveAttribute(ctx, state, "content");
234 saveAttribute(ctx, state, "def");
235 saveAttribute(ctx, state, "state");
236 saveAttribute(ctx, state, "model");
237 saveAttribute(ctx, state, "actionResult");
238
239 return state;
240 }
241
242 protected void saveAttribute(final Map<String, Object> ctx, Map<String, Object> state, String name) {
243 state.put(name, ctx.get(name));
244 }
245
246 protected void restoreContext(final Map<String, Object> ctx, Map<String, Object> state) {
247 for (Entry<String, Object> entry : state.entrySet()) {
248 setContextAttribute(ctx, entry.getKey(), entry.getValue());
249 }
250 }
251
252 protected void setupContext(final Map<String, Object> ctx, Node content, RenderableDefinition definition, RenderingModel<?> model, Object actionResult){
253 final Node mainContent = getMainContentSafely(content);
254
255 setContextAttribute(ctx, "content", content != null ? new ContentMap(wrapNodeForTemplate(content)) : null);
256 setContextAttribute(ctx, "def", definition);
257 setContextAttribute(ctx, "state", getAggregationStateSafely());
258 setContextAttribute(ctx, "model", model);
259 setContextAttribute(ctx, "actionResult", actionResult);
260
261 for (Entry<String, ContextAttributeConfiguration> entry : contextAttributes.entrySet()) {
262 setContextAttribute(ctx, entry.getKey(), Components.getComponent(entry.getValue().getComponentClass()));
263 }
264
265 }
266
267
268
269
270 protected Node getMainContentSafely(Node content) {
271 AggregationState state = getAggregationStateSafely();
272 return state == null ? content : state.getMainContentNode();
273 }
274
275
276
277
278 protected AggregationState getAggregationStateSafely() {
279 if(MgnlContext.isWebContext()){
280 return MgnlContext.getAggregationState();
281 }
282 return null;
283 }
284
285
286
287
288
289
290 protected Node wrapNodeForModel(Node content) {
291 NodeUtil.deepUnwrap(content, HTMLEscapingNodeWrapper.class);
292 content = wrapWithChannelVisibilityWrapper(content);
293 content = wrapWithI18NWrapper(content);
294 return content;
295 }
296
297
298
299
300
301
302
303 protected Node wrapNodeForTemplate(Node content) {
304 content = wrapWithChannelVisibilityWrapper(content);
305 content = wrapWithI18NWrapper(content);
306 content = wrapWithHTMLEscapingWrapper(content);
307 return content;
308 }
309
310 private Node wrapWithHTMLEscapingWrapper(Node content) {
311 if(!NodeUtil.isWrappedWith(content, HTMLEscapingNodeWrapper.class)){
312 content = new HTMLEscapingNodeWrapper(content, true);
313 }
314 return content;
315 }
316
317 private Node wrapWithI18NWrapper(Node content) {
318 if(!NodeUtil.isWrappedWith(content, I18nNodeWrapper.class)){
319 content = new I18nNodeWrapper(content);
320 }
321 return content;
322 }
323
324 private Node wrapWithChannelVisibilityWrapper(Node content) {
325
326 if (ContentDecoratorUtil.isDecoratedWith(content, ChannelVisibilityContentDecorator.class)) {
327 return content;
328 }
329 AggregationState aggregationState = getAggregationStateSafely();
330 if (aggregationState == null) {
331 return content;
332 }
333 String channel = aggregationState.getChannel().getName();
334 if (StringUtils.isEmpty(channel) || channel.equalsIgnoreCase("all")) {
335 return content;
336 }
337 return new ChannelVisibilityContentDecorator(channel).wrapNode(content);
338 }
339
340 protected Object setContextAttribute(final Map<String, Object> ctx, final String name, Object value) {
341 return ctx.put(name, value);
342 }
343
344 public Map<String, ContextAttributeConfiguration> getContextAttributes() {
345 return contextAttributes;
346 }
347
348 public void setContextAttributes(Map<String, ContextAttributeConfiguration> contextAttributes) {
349 if(this.contextAttributes!=null) {
350 this.contextAttributes.putAll(contextAttributes);
351 } else {
352 this.contextAttributes = contextAttributes;
353 }
354 }
355
356 public void addContextAttribute(String name, ContextAttributeConfiguration contextAttributeConfiguration){
357 this.contextAttributes.put(name, contextAttributeConfiguration);
358 }
359
360
361
362
363 protected abstract Map<String, Object> newContext();
364
365
366
367
368 protected abstract void onRender(Node content, RenderableDefinition definition, RenderingContext renderingCtx, Map<String, Object> ctx, String templateScript) throws RenderException;
369
370 }