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.getParameters();
203 if (params != null) {
204 BeanUtils.populate(model, params);
205 }
206
207 return model;
208
209 } catch (MgnlInstantiationException e) {
210 throw new RenderException("Can't instantiate model: " + modelClass, e);
211 } catch (InvocationTargetException e) {
212 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
213 } catch (IllegalAccessException e) {
214 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
215 }
216 }
217
218 protected Map<String, Object> saveContextState(final Map<String, Object> ctx) {
219 Map<String, Object> state = new HashMap<String, Object>();
220
221 saveAttribute(ctx, state, "content");
222 saveAttribute(ctx, state, "def");
223 saveAttribute(ctx, state, "state");
224 saveAttribute(ctx, state, "model");
225 saveAttribute(ctx, state, "actionResult");
226
227 return state;
228 }
229
230 protected void saveAttribute(final Map<String, Object> ctx, Map<String, Object> state, String name) {
231 state.put(name, ctx.get(name));
232 }
233
234 protected void restoreContext(final Map<String, Object> ctx, Map<String, Object> state) {
235 for (Entry<String, Object> entry : state.entrySet()) {
236 setContextAttribute(ctx, entry.getKey(), entry.getValue());
237 }
238 }
239
240 protected void setupContext(final Map<String, Object> ctx, Node content, RenderableDefinition definition, RenderingModel<?> model, Object actionResult){
241 final Node mainContent = getMainContentSafely(content);
242
243 setContextAttribute(ctx, "content", content != null ? new ContentMap(wrapNodeForTemplate(content)) : null);
244 setContextAttribute(ctx, "def", definition);
245 setContextAttribute(ctx, "state", getAggregationStateSafely());
246 setContextAttribute(ctx, "model", model);
247 setContextAttribute(ctx, "actionResult", actionResult);
248
249 for (Entry<String, ContextAttributeConfiguration> entry : contextAttributes.entrySet()) {
250 setContextAttribute(ctx, entry.getKey(), Components.getComponent(entry.getValue().getComponentClass()));
251 }
252
253 }
254
255
256
257
258 protected Node getMainContentSafely(Node content) {
259 AggregationState state = getAggregationStateSafely();
260 return state == null ? content : state.getMainContent().getJCRNode();
261 }
262
263
264
265
266 protected AggregationState getAggregationStateSafely() {
267 if(MgnlContext.isWebContext()){
268 return MgnlContext.getAggregationState();
269 }
270 return null;
271 }
272
273
274
275
276
277
278 protected Node wrapNodeForModel(Node content) {
279 NodeUtil.deepUnwrap(content, HTMLEscapingNodeWrapper.class);
280 content = wrapWithChannelVisibilityWrapper(content);
281 content = wrapWithI18NWrapper(content);
282 return content;
283 }
284
285
286
287
288
289
290
291 protected Node wrapNodeForTemplate(Node content) {
292 content = wrapWithChannelVisibilityWrapper(content);
293 content = wrapWithI18NWrapper(content);
294 content = wrapWithHTMLEscapingWrapper(content);
295 return content;
296 }
297
298 private Node wrapWithHTMLEscapingWrapper(Node content) {
299 if(!NodeUtil.isWrappedWith(content, HTMLEscapingNodeWrapper.class)){
300 content = new HTMLEscapingNodeWrapper(content, true);
301 }
302 return content;
303 }
304
305 private Node wrapWithI18NWrapper(Node content) {
306 if(!NodeUtil.isWrappedWith(content, I18nNodeWrapper.class)){
307 content = new I18nNodeWrapper(content);
308 }
309 return content;
310 }
311
312 private Node wrapWithChannelVisibilityWrapper(Node content) {
313
314 if (ContentDecoratorUtil.isDecoratedWith(content, ChannelVisibilityContentDecorator.class)) {
315 return content;
316 }
317 AggregationState aggregationState = getAggregationStateSafely();
318 if (aggregationState == null) {
319 return content;
320 }
321 String channel = aggregationState.getChannel().getName();
322 if (StringUtils.isEmpty(channel) || channel.equalsIgnoreCase("all")) {
323 return content;
324 }
325 return new ChannelVisibilityContentDecorator(channel).wrapNode(content);
326 }
327
328 protected Object setContextAttribute(final Map<String, Object> ctx, final String name, Object value) {
329 return ctx.put(name, value);
330 }
331
332 public Map<String, ContextAttributeConfiguration> getContextAttributes() {
333 return contextAttributes;
334 }
335
336 public void setContextAttributes(Map<String, ContextAttributeConfiguration> contextAttributes) {
337 if(this.contextAttributes!=null) {
338 this.contextAttributes.putAll(contextAttributes);
339 } else {
340 this.contextAttributes = contextAttributes;
341 }
342 }
343
344 public void addContextAttribute(String name, ContextAttributeConfiguration contextAttributeConfiguration){
345 this.contextAttributes.put(name, contextAttributeConfiguration);
346 }
347
348
349
350
351 protected abstract Map<String, Object> newContext();
352
353
354
355
356 protected abstract void onRender(Node content, RenderableDefinition definition, RenderingContext renderingCtx, Map<String, Object> ctx, String templateScript) throws RenderException;
357
358 }