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