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