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