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.templating.elements;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.cms.core.MetaData;
38 import info.magnolia.cms.core.MgnlNodeType;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.context.WebContext;
41 import info.magnolia.jcr.predicate.AbstractPredicate;
42 import info.magnolia.jcr.util.ContentMap;
43 import info.magnolia.jcr.util.NodeUtil;
44 import info.magnolia.jcr.wrapper.InheritanceNodeWrapper;
45 import info.magnolia.objectfactory.Components;
46 import info.magnolia.rendering.context.RenderingContext;
47 import info.magnolia.rendering.engine.AppendableOnlyOutputProvider;
48 import info.magnolia.rendering.engine.RenderException;
49 import info.magnolia.rendering.engine.RenderingEngine;
50 import info.magnolia.rendering.template.AreaDefinition;
51 import info.magnolia.rendering.template.AutoGenerationConfiguration;
52 import info.magnolia.rendering.template.ComponentAvailability;
53 import info.magnolia.rendering.template.RenderableDefinition;
54 import info.magnolia.rendering.template.TemplateDefinition;
55 import info.magnolia.rendering.template.configured.ConfiguredAreaDefinition;
56
57 import java.io.IOException;
58 import java.util.ArrayList;
59 import java.util.HashMap;
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Map;
63
64 import javax.jcr.Node;
65 import javax.jcr.RepositoryException;
66
67 import org.apache.commons.lang.StringUtils;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71
72
73
74
75
76
77 public class AreaElement extends AbstractContentTemplatingElement {
78
79 public static final String CMS_AREA = "cms:area";
80
81 public static final String ATTRIBUTE_COMPONENT = "component";
82 public static final String ATTRIBUTE_COMPONENTS = "components";
83
84 private final RenderingEngine renderingEngine;
85
86 private Node parentNode;
87 private Node areaNode;
88 private TemplateDefinition templateDefinition;
89 private AreaDefinition areaDefinition;
90 private String name;
91 private String type;
92 private String dialog;
93 private String availableComponents;
94 private String label;
95 private boolean inherit;
96
97 private Map<String, Object> contextAttributes = new HashMap<String, Object>();
98
99 private boolean commentClosed = false;
100
101 public AreaElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine) {
102 super(server, renderingContext);
103 this.renderingEngine = renderingEngine;
104 }
105
106 @Override
107 public void begin(Appendable out) throws IOException, RenderException {
108 this.parentNode = getTargetContent();
109
110 this.templateDefinition = resolveTemplateDefinition();
111 this.areaDefinition = resolveAreaDefinition();
112
113
114 this.name = resolveName();
115 this.dialog = resolveDialog();
116 this.type = resolveType();
117 this.label = resolveLabel();
118 this.availableComponents = resolveAvailableComponents();
119 this.inherit = isInheritanceEnabled();
120
121
122 if(areaDefinition == null){
123 buildAdHocAreaDefinition();
124 }
125
126 this.areaNode = resolveAreaNode();
127
128 if (isAdmin()) {
129 MarkupHelper helper = new MarkupHelper(out);
130
131 helper.openComment(CMS_AREA).attribute("content", getNodePath(parentNode));
132 helper.attribute("name", this.name);
133 helper.attribute("availableComponents", this.availableComponents);
134 helper.attribute("type", this.type);
135 helper.attribute("dialog", this.dialog);
136 helper.attribute("label", this.label);
137 helper.attribute("inherit", String.valueOf(this.inherit));
138 helper.attribute("optional", String.valueOf(this.areaDefinition.isOptional()));
139 if(isOptionalAreaCreated()) {
140 helper.attribute("created", "true");
141 }
142 helper.attribute("showAddButton", String.valueOf(shouldShowAddButton()));
143 helper.append(" -->\n");
144
145 }
146 }
147
148 private Node createNewAreaNode() throws RepositoryException {
149 final String parentId = this.parentNode.getIdentifier();
150 final String workspaceName = this.parentNode.getSession().getWorkspace().getName();
151
152 MgnlContext.doInSystemContext(new MgnlContext.Op<Void, RepositoryException>() {
153 @Override
154 public Void exec() throws RepositoryException {
155 Node parentNodeInSystemSession = NodeUtil.getNodeByIdentifier(workspaceName, parentId);
156 Node newAreaNode = NodeUtil.createPath(parentNodeInSystemSession, AreaElement.this.name, MgnlNodeType.NT_AREA);
157 NodeUtil.createPath(newAreaNode, MetaData.DEFAULT_META_NODE, MgnlNodeType.NT_METADATA);
158 newAreaNode.getSession().save();
159 return null;
160 }
161 });
162 return this.parentNode.getNode(this.name);
163 }
164
165 protected void buildAdHocAreaDefinition() {
166 ConfiguredAreaDefinition addHocAreaDefinition = new ConfiguredAreaDefinition();
167 addHocAreaDefinition.setName(this.name);
168 addHocAreaDefinition.setDialog(this.dialog);
169 addHocAreaDefinition.setType(this.type);
170 addHocAreaDefinition.setRenderType(this.templateDefinition.getRenderType());
171 areaDefinition = addHocAreaDefinition;
172 }
173
174 @Override
175 public void end(Appendable out) throws RenderException {
176 try {
177 if (isEnabled()) {
178 Map<String, Object> contextObjects = new HashMap<String, Object>();
179
180 List<ContentMap> components = new ArrayList<ContentMap>();
181
182 if (areaNode != null) {
183 for (Node node : NodeUtil.getNodes(areaNode, MgnlNodeType.NT_COMPONENT)) {
184 components.add(new ContentMap(node));
185 }
186 }
187 if(AreaDefinition.TYPE_SINGLE.equals(type)) {
188 if(components.size() > 1) {
189 throw new RenderException("Can't render single area [" + areaNode + "]: expected one component node but found more.");
190 }
191 if(components.size() == 1) {
192 contextObjects.put(ATTRIBUTE_COMPONENT, components.get(0));
193 } else {
194 contextObjects.put(ATTRIBUTE_COMPONENT, null);
195 }
196 } else {
197 contextObjects.put(ATTRIBUTE_COMPONENTS, components);
198 }
199
200
201 if(areaDefinition.getRenderType() == null && areaDefinition instanceof ConfiguredAreaDefinition){
202 ((ConfiguredAreaDefinition)areaDefinition).setRenderType(this.templateDefinition.getRenderType());
203 }
204
205
206
207 if(areaDefinition.getI18nBasename() == null && areaDefinition instanceof ConfiguredAreaDefinition){
208 ((ConfiguredAreaDefinition)areaDefinition).setI18nBasename(this.templateDefinition.getI18nBasename());
209 }
210
211 WebContext webContext = MgnlContext.getWebContext();
212 webContext.push(webContext.getRequest(), webContext.getResponse());
213 setAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
214 try {
215 renderingEngine.render(areaNode, areaDefinition, contextObjects, new AppendableOnlyOutputProvider(out));
216 } finally {
217 webContext.pop();
218 webContext.setPageContext(null);
219 restoreAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
220 }
221
222 }
223
224 if (isAdmin()) {
225 MarkupHelper helper = new MarkupHelper(out);
226 helper.closeComment(CMS_AREA);
227 }
228 } catch (Exception e) {
229 throw new RenderException("Can't render area " + areaNode + " with name " + this.name, e);
230 }
231 }
232
233 protected Node resolveAreaNode() throws RenderException {
234 final Node content = getTargetContent();
235 Node area = null;
236 try {
237 if(content.hasNode(name)){
238 area = content.getNode(name);
239 } else {
240
241 if(!areaDefinition.isOptional()) {
242 area = createNewAreaNode();
243 }
244 }
245 }
246 catch (RepositoryException e) {
247 throw new RenderException("Can't access area node [" + name + "] on [" + content + "]", e);
248 }
249
250 if(area != null) {
251
252 final AutoGenerationConfiguration autoGeneration = areaDefinition.getAutoGeneration();
253 if (autoGeneration != null && autoGeneration.getGeneratorClass() != null) {
254 Components.newInstance(autoGeneration.getGeneratorClass(), area).generate(autoGeneration);
255 }
256 if(isInherit()) {
257 area = new InheritanceNodeWrapper(area, new InheritablePredicate(area));
258 }
259 }
260 return area;
261 }
262
263 protected AreaDefinition resolveAreaDefinition() {
264 if (areaDefinition != null) {
265 return areaDefinition;
266 }
267
268 if (!StringUtils.isEmpty(name)) {
269 if (templateDefinition != null && templateDefinition.getAreas().containsKey(name)) {
270 return templateDefinition.getAreas().get(name);
271 }
272 }
273
274
275 return null;
276 }
277
278 protected TemplateDefinition resolveTemplateDefinition() throws RenderException {
279 final RenderableDefinition renderableDefinition = getRenderingContext().getRenderableDefinition();
280 if (renderableDefinition == null || renderableDefinition instanceof TemplateDefinition) {
281 return (TemplateDefinition) renderableDefinition;
282 }
283 throw new RenderException("Current RenderableDefinition [" + renderableDefinition + "] is not of type TemplateDefinition. Areas cannot be supported");
284 }
285
286 private boolean isEnabled() {
287 return areaDefinition != null && areaDefinition.isEnabled();
288 }
289
290 private String resolveDialog() {
291 return dialog != null ? dialog : areaDefinition != null ? areaDefinition.getDialog() : null;
292 }
293
294 private String resolveType() {
295 return type != null ? type : areaDefinition != null && areaDefinition.getType() != null ? areaDefinition.getType() : AreaDefinition.DEFAULT_TYPE;
296 }
297
298 private String resolveName() {
299 return name != null ? name : (areaDefinition != null ? areaDefinition.getName() : null);
300 }
301
302 private String resolveLabel() {
303 return label != null ? label : (areaDefinition != null && StringUtils.isNotBlank(areaDefinition.getTitle()) ? areaDefinition.getTitle() : StringUtils.capitalize(name));
304 }
305
306 private boolean isInheritanceEnabled() {
307 return areaDefinition != null && areaDefinition.getInheritance() != null && areaDefinition.getInheritance().isEnabled();
308 }
309
310 private boolean isOptionalAreaCreated() {
311 return this.areaDefinition.isOptional() && this.areaNode != null;
312 }
313
314 private boolean hasComponents(Node parent) throws RenderException {
315 try {
316 return NodeUtil.getNodes(parent, MgnlNodeType.NT_COMPONENT).iterator().hasNext();
317 } catch (RepositoryException e) {
318 throw new RenderException(e);
319 }
320 }
321
322 protected String resolveAvailableComponents() {
323 if (StringUtils.isNotEmpty(availableComponents)) {
324 return availableComponents;
325 }
326 if (areaDefinition != null && areaDefinition.getAvailableComponents().size() > 0) {
327 Iterator<ComponentAvailability> iterator = areaDefinition.getAvailableComponents().values().iterator();
328 List<String> componentIds = new ArrayList<String>();
329 while (iterator.hasNext()) {
330 ComponentAvailability availableComponent = iterator.next();
331 if(availableComponent.isEnabled()) {
332 componentIds.add(availableComponent.getId());
333 }
334 }
335 return StringUtils.join(componentIds, ',');
336 }
337 return "";
338 }
339
340 private boolean shouldShowAddButton() throws RenderException {
341
342 if(areaNode == null || type.equals(AreaDefinition.TYPE_NO_COMPONENT) || (type.equals(AreaDefinition.TYPE_SINGLE) && hasComponents(areaNode))) {
343 return false;
344 }
345
346 return true;
347 }
348
349 public String getName() {
350 return name;
351 }
352
353 public void setName(String name) {
354 this.name = name;
355 }
356
357 public AreaDefinition getArea() {
358 return areaDefinition;
359 }
360
361 public void setArea(AreaDefinition area) {
362 this.areaDefinition = area;
363 }
364
365 public String getAvailableComponents() {
366 return availableComponents;
367 }
368
369 public void setAvailableComponents(String availableComponents) {
370 this.availableComponents = availableComponents;
371 }
372
373 public String getType() {
374 return type;
375 }
376
377 public void setType(String type) {
378 this.type = type;
379 }
380
381 public String getDialog() {
382 return dialog;
383 }
384
385 public void setDialog(String dialog) {
386 this.dialog = dialog;
387 }
388
389 public boolean isInherit() {
390 return inherit;
391 }
392
393 public void setInherit(boolean inherit) {
394 this.inherit = inherit;
395 }
396
397 public Map<String, Object> getContextAttributes() {
398 return contextAttributes;
399 }
400
401 public void setContextAttributes(Map<String, Object> contextAttributes) {
402 this.contextAttributes = contextAttributes;
403 }
404
405
406
407
408
409
410
411 private static class InheritablePredicate extends AbstractPredicate<Node> {
412
413 private static final Logger log = LoggerFactory.getLogger(AreaElement.class);
414
415 private Node root;
416
417 public InheritablePredicate(Node root) {
418 this.root = root;
419 }
420
421 @Override
422 public boolean evaluateTyped(Node t) {
423
424
425 return true;
426
427
428
429
430
431
432
433
434
435
436
437
438
439 }
440 }
441 }