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.util.ContentMap;
39 import info.magnolia.jcr.util.NodeUtil;
40 import info.magnolia.jcr.wrapper.ChannelVisibilityContentDecorator;
41 import info.magnolia.jcr.wrapper.HTMLEscapingNodeWrapper;
42 import info.magnolia.jcr.wrapper.I18nNodeWrapper;
43 import info.magnolia.objectfactory.Components;
44 import info.magnolia.objectfactory.MgnlInstantiationException;
45 import info.magnolia.objectfactory.ParameterInfo;
46 import info.magnolia.objectfactory.ParameterResolver;
47 import info.magnolia.rendering.context.RenderingContext;
48 import info.magnolia.rendering.engine.RenderException;
49 import info.magnolia.rendering.model.EarlyExecutionAware;
50 import info.magnolia.rendering.model.ModelExecutionFilter;
51 import info.magnolia.rendering.model.RenderingModel;
52 import info.magnolia.rendering.model.RenderingModelImpl;
53 import info.magnolia.rendering.template.RenderableDefinition;
54
55 import java.lang.reflect.InvocationTargetException;
56 import java.util.HashMap;
57 import java.util.Map;
58 import java.util.Map.Entry;
59
60 import javax.jcr.Node;
61 import javax.jcr.RepositoryException;
62
63 import org.apache.commons.beanutils.BeanUtils;
64 import org.apache.commons.lang.StringUtils;
65 import org.apache.commons.lang.exception.ExceptionUtils;
66
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 onRender(content, definition, renderingCtx, ctx, templatePath);
137 MgnlContext.setAttribute(MODEL_ATTRIBUTE, parentModel);
138
139 restoreContext(ctx, savedContextState);
140 }
141
142
143
144
145
146
147
148
149 protected String resolveTemplateScript(Node content, RenderableDefinition definition, RenderingModel<?> model, final String actionResult) {
150 return definition.getTemplateScript();
151 }
152
153
154
155
156
157
158 @Override
159 public RenderingModel<?> newModel(final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
160
161 Class clazz = definition.getModelClass();
162
163
164 if (clazz == null) {
165 clazz = RenderingModelImpl.class;
166 }
167
168 final Node wrappedContent = wrapNodeForModel(content);
169
170 return newModel(clazz, wrappedContent, definition, parentModel);
171 }
172
173 protected <T extends RenderingModel<?>> T newModel(Class<T> modelClass, final Node content, final RenderableDefinition definition, final RenderingModel<?> parentModel) throws RenderException {
174
175 try {
176
177 T model = Components.getComponentProvider().newInstanceWithParameterResolvers(modelClass,
178 new ParameterResolver() {
179 @Override
180 public Object resolveParameter(ParameterInfo parameter) {
181 if (parameter.getParameterType().equals(Node.class)) {
182 return content;
183 }
184 if (parameter.getParameterType().isAssignableFrom(definition.getClass())) {
185 return definition;
186 }
187 if (parameter.getParameterType().equals(RenderingModel.class)) {
188 return parentModel;
189 }
190 return UNRESOLVED;
191 }
192 }
193 );
194
195
196 Map<String, String> params = MgnlContext.getParameters();
197 if (params != null) {
198 BeanUtils.populate(model, params);
199 }
200
201 return model;
202
203 } catch (MgnlInstantiationException e) {
204 throw new RenderException("Can't instantiate model: " + modelClass, e);
205 } catch (InvocationTargetException e) {
206 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
207 } catch (IllegalAccessException e) {
208 throw new RenderException("Can't create rendering model: " + ExceptionUtils.getRootCauseMessage(e), e);
209 }
210 }
211
212 protected Map<String, Object> saveContextState(final Map<String, Object> ctx) {
213 Map<String, Object> state = new HashMap<String, Object>();
214
215 saveAttribute(ctx, state, "content");
216 saveAttribute(ctx, state, "def");
217 saveAttribute(ctx, state, "state");
218 saveAttribute(ctx, state, "model");
219 saveAttribute(ctx, state, "actionResult");
220
221 return state;
222 }
223
224 protected void saveAttribute(final Map<String, Object> ctx, Map<String, Object> state, String name) {
225 state.put(name, ctx.get(name));
226 }
227
228 protected void restoreContext(final Map<String, Object> ctx, Map<String, Object> state) {
229 for (Entry<String, Object> entry : state.entrySet()) {
230 setContextAttribute(ctx, entry.getKey(), entry.getValue());
231 }
232 }
233
234 protected void setupContext(final Map<String, Object> ctx, Node content, RenderableDefinition definition, RenderingModel<?> model, Object actionResult){
235 final Node mainContent = getMainContentSafely(content);
236
237 setContextAttribute(ctx, "content", content != null ? new ContentMap(wrapNodeForTemplate(content)) : null);
238 setContextAttribute(ctx, "def", definition);
239 setContextAttribute(ctx, "state", getAggregationStateSafely());
240 setContextAttribute(ctx, "model", model);
241 setContextAttribute(ctx, "actionResult", actionResult);
242
243 for (Entry<String, ContextAttributeConfiguration> entry : contextAttributes.entrySet()) {
244 setContextAttribute(ctx, entry.getKey(), Components.getComponent(entry.getValue().getComponentClass()));
245 }
246
247 }
248
249
250
251
252 protected Node getMainContentSafely(Node content) {
253 AggregationState state = getAggregationStateSafely();
254 return state == null ? content : state.getMainContent().getJCRNode();
255 }
256
257
258
259
260 protected AggregationState getAggregationStateSafely() {
261 if(MgnlContext.isWebContext()){
262 return MgnlContext.getAggregationState();
263 }
264 return null;
265 }
266
267
268
269
270
271 protected Node wrapNodeForModel(Node content) {
272 content = wrapWithChannelVisibilityWrapper(content);
273 content = wrapWithI18NWrapper(content);
274 NodeUtil.deepUnwrap(content, HTMLEscapingNodeWrapper.class);
275 return content;
276 }
277
278 protected Node wrapNodeForTemplate(Node content) {
279 content = wrapWithChannelVisibilityWrapper(content);
280 content = wrapWithI18NWrapper(content);
281 content = wrapWithHTMLEscapingWrapper(content);
282 return content;
283 }
284
285 private Node wrapWithHTMLEscapingWrapper(Node content) {
286 if(!NodeUtil.isWrappedWith(content, HTMLEscapingNodeWrapper.class)){
287 content = new HTMLEscapingNodeWrapper(content, true);
288 }
289 return content;
290 }
291
292 private Node wrapWithI18NWrapper(Node content) {
293 if(!NodeUtil.isWrappedWith(content, I18nNodeWrapper.class)){
294 content = new I18nNodeWrapper(content);
295 }
296 return content;
297 }
298
299 private Node wrapWithChannelVisibilityWrapper(Node content) {
300 AggregationState aggregationState = getAggregationStateSafely();
301 if (aggregationState == null) {
302 return content;
303 }
304 String channel = aggregationState.getChannel().getName();
305 if (StringUtils.isEmpty(channel) || channel.equalsIgnoreCase("all")) {
306 return content;
307 }
308 return new ChannelVisibilityContentDecorator(channel).wrapNode(content);
309 }
310
311 protected Object setContextAttribute(final Map<String, Object> ctx, final String name, Object value) {
312 return ctx.put(name, value);
313 }
314
315 public Map<String, ContextAttributeConfiguration> getContextAttributes() {
316 return contextAttributes;
317 }
318
319 public void setContextAttributes(Map<String, ContextAttributeConfiguration> contextAttributes) {
320 if(this.contextAttributes!=null) {
321 this.contextAttributes.putAll(contextAttributes);
322 } else {
323 this.contextAttributes = contextAttributes;
324 }
325 }
326
327 public void addContextAttribute(String name, ContextAttributeConfiguration contextAttributeConfiguration){
328 this.contextAttributes.put(name, contextAttributeConfiguration);
329 }
330
331
332
333
334 protected abstract Map<String, Object> newContext();
335
336
337
338
339 protected abstract void onRender(Node content, RenderableDefinition definition, RenderingContext renderingCtx, Map<String, Object> ctx, String templateScript) throws RenderException;
340
341 }