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