View Javadoc
1   /**
2    * This file Copyright (c) 2011-2016 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.templating.elements;
35  
36  import info.magnolia.beanmerger.BeanMergerUtil;
37  import info.magnolia.cms.beans.config.ServerConfiguration;
38  import info.magnolia.context.MgnlContext;
39  import info.magnolia.context.WebContext;
40  import info.magnolia.i18nsystem.I18nizer;
41  import info.magnolia.jcr.RuntimeRepositoryException;
42  import info.magnolia.jcr.util.ContentMap;
43  import info.magnolia.jcr.util.NodeTypes;
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.generator.Generator;
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.rendering.template.variation.RenderableVariationResolver;
58  import info.magnolia.templating.freemarker.AbstractDirective;
59  import info.magnolia.templating.inheritance.DefaultInheritanceContentDecorator;
60  import info.magnolia.templating.renderers.NoScriptRenderer;
61  
62  import java.io.IOException;
63  import java.util.ArrayList;
64  import java.util.Collection;
65  import java.util.HashMap;
66  import java.util.Iterator;
67  import java.util.List;
68  import java.util.Map;
69  
70  import javax.inject.Inject;
71  import javax.jcr.Node;
72  import javax.jcr.PathNotFoundException;
73  import javax.jcr.RepositoryException;
74  import javax.jcr.Session;
75  import javax.jcr.lock.LockException;
76  
77  import org.apache.commons.collections4.CollectionUtils;
78  import org.apache.commons.lang3.StringUtils;
79  import org.slf4j.Logger;
80  import org.slf4j.LoggerFactory;
81  
82  /**
83   * Renders an area and outputs a marker that instructs the page editor to place a bar at this location.
84   */
85  public class AreaElement extends AbstractContentTemplatingElement {
86  
87      private static final Logger log = LoggerFactory.getLogger(AreaElement.class);
88      public static final String CMS_AREA = "cms:area";
89  
90      public static final String ATTRIBUTE_COMPONENT = "component";
91      public static final String ATTRIBUTE_COMPONENTS = "components";
92  
93      private final RenderingEngine renderingEngine;
94  
95      private Node areaNode;
96      private TemplateDefinition templateDefinition;
97      private AreaDefinition areaDefinition;
98      private String name;
99      private String type;
100     private String dialog;
101     private String availableComponents;
102     private String label;
103     private String description;
104     private Boolean inherit;
105     private Boolean optional;
106     private Boolean editable;
107     private Integer maxComponents;
108     private Boolean createAreaNode;
109 
110     private Map<String, Object> contextAttributes = new HashMap<String, Object>();
111 
112     private String areaPath;
113 
114     private boolean isAreaDefinitionEnabled;
115     private final RenderableVariationResolver variationResolver;
116 
117     @Inject
118     public AreaElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, RenderableVariationResolver variationResolver, I18nizer i18nizer) {
119         super(server, renderingContext, i18nizer);
120         this.renderingEngine = renderingEngine;
121         this.variationResolver = variationResolver;
122     }
123 
124     /**
125      * @deprecated since 5.4.4, use {@link #AreaElement(info.magnolia.cms.beans.config.ServerConfiguration, info.magnolia.rendering.context.RenderingContext, info.magnolia.rendering.engine.RenderingEngine, info.magnolia.rendering.template.variation.RenderableVariationResolver, info.magnolia.i18nsystem.I18nizer)} instead.
126      */
127     @Deprecated
128     public AreaElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, RenderableVariationResolver variationResolver) {
129         this(server, renderingContext, renderingEngine, Components.getComponent(RenderableVariationResolver.class), Components.getComponent(I18nizer.class));
130     }
131 
132     @Override
133     public void begin(Appendable out) throws IOException, RenderException {
134 
135         this.templateDefinition = resolveTemplateDefinition();
136         this.areaDefinition = resolveAreaDefinition();
137 
138         this.isAreaDefinitionEnabled = areaDefinition != null && (areaDefinition.getEnabled() == null || areaDefinition.getEnabled());
139 
140         if (!this.isAreaDefinitionEnabled) {
141             return;
142         }
143         // set the values based on the area definition if not passed
144         this.name = resolveName();
145         this.dialog = resolveDialog();
146         this.type = resolveType();
147         this.availableComponents = resolveAvailableComponents();
148         this.inherit = isInheritanceEnabled();
149         this.optional = resolveOptional();
150         this.editable = resolveEditable();
151         this.createAreaNode = resolveCreateAreaNode();
152 
153         // build an adhoc area definition if no area definition can be resolved
154         if (this.areaDefinition == null) {
155             buildAdHocAreaDefinition();
156         }
157 
158         this.maxComponents = resolveMaximumOfComponents();
159 
160         // read area node and calculate the area path
161         this.areaNode = getPassedContent();
162         if (this.areaNode != null) {
163             this.areaPath = getNodePath(areaNode);
164         } else {
165             // will be null if no area has been created (for instance for optional areas)
166             // current content is the parent node
167             Node parentNode = currentContent();
168             if (createAreaNode) {
169                 this.areaNode = tryToCreateAreaNode(parentNode);
170                 this.areaPath = getNodePath(parentNode) + "/" + name;
171             } else {
172                 this.areaNode = parentNode;
173                 this.areaPath = getNodePath(parentNode);
174             }
175         }
176 
177         if (renderComments()) { // add condition into renderComments() method when adding extra condition to make sure it's in sync with adding comments in end() method
178             MarkupHelper helper = new MarkupHelper(out);
179 
180             helper.openComment(CMS_AREA).attribute(AbstractDirective.CONTENT_ATTRIBUTE, this.areaPath);
181             helper.attribute("name", this.name);
182             helper.attribute("availableComponents", this.availableComponents);
183             helper.attribute("type", this.type);
184             helper.attribute("dialog", this.dialog);
185 
186             this.label = resolveLabel(); //this is resolved here instead of the beginning of this method since it uses i18nizer proxy and this is useless when not rendering page editor comments (outside of admincentral) and could cause performance issues when proxied also outside of admincentral
187             helper.attribute("label", this.label);
188 
189             helper.attribute("inherit", String.valueOf(this.inherit));
190             if (this.editable != null) {
191                 helper.attribute("editable", String.valueOf(this.editable));
192             }
193             helper.attribute("optional", String.valueOf(this.optional));
194             if (isOptionalAreaCreated()) {
195                 helper.attribute("created", "true");
196             }
197             helper.attribute("createAreaNode", String.valueOf(this.createAreaNode));
198             helper.attribute("showAddButton", String.valueOf(shouldShowAddButton()));
199             helper.attribute("showNewComponentArea", String.valueOf(this.shouldShowAddNewComponent()));
200 
201             this.description = this.i18nize(templateDefinition).getDescription();
202             //this was already translated with i18nizer. This is here just to keep compatibility with old i18n mechanism. Remove when we drop support of old i18n
203             final String i18nBasename = StringUtils.isNotEmpty(areaDefinition.getI18nBasename()) ? areaDefinition.getI18nBasename() : templateDefinition.getI18nBasename();
204             description = this.legacyTranslate(description, i18nBasename);
205 
206             if (StringUtils.isNotBlank(description)) {
207                 helper.attribute("description", this.description);
208             }
209 
210             helper.attribute("activationStatus", getActivationStatus(this.areaNode));
211 
212             helper.append(" -->\n");
213 
214         }
215     }
216 
217     private boolean hasPermission(Node node) {
218         if (node == null) {
219             node = currentContent();
220         }
221         try {
222             return node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY);
223         } catch (RepositoryException e) {
224             log.error("Could not determine permission for node {}", node);
225         }
226         return false;
227     }
228 
229     private Node createNewAreaNode(Node parentNode) throws RepositoryException {
230         final String parentId = parentNode.getIdentifier();
231         final String workspaceName = parentNode.getSession().getWorkspace().getName();
232         try {
233             // we need to lock the page to prevent concurrent editing, not just the parent of area node we are about to create (think of nested areas)
234             MgnlContext.doInSystemContext(new MgnlContext.LockingOp(workspaceName, parentNode.getPath(), NodeTypes.Page.NAME) {
235 
236                 @Override
237                 public void doExec() throws RepositoryException {
238                     Node parentNodeInSystemSession = NodeUtil.getNodeByIdentifier(workspaceName, parentId);
239                     Node newAreaNode = NodeUtil.createPath(parentNodeInSystemSession, AreaElement.this.name, NodeTypes.Area.NAME);
240                     newAreaNode.getSession().save();
241                 }
242             });
243         } catch (LockException e) {
244             // maybe server is too busy, but that could also mean that someone else created our are in the mean time.
245             log.warn("Failed to create area due to locking problem. {}", e.getMessage(), e);
246         } catch (PathNotFoundException e) {
247             // page or the area was deleted while we attempted to obtain a lock on it
248             log.warn("Failed to create area due to concurrent deletion of page or the parent area. {}", e.getMessage(), e);
249         }
250         // JR will force refresh, some other JCRs might not
251         parentNode.getSession().refresh(true);
252         return parentNode.getNode(this.name);
253     }
254 
255     protected void buildAdHocAreaDefinition() {
256         ConfiguredAreaDefinition addHocAreaDefinition = new ConfiguredAreaDefinition();
257         addHocAreaDefinition.setName(this.name);
258         addHocAreaDefinition.setDialog(this.dialog);
259         addHocAreaDefinition.setType(this.type);
260         addHocAreaDefinition.setRenderType(this.templateDefinition.getRenderType());
261         areaDefinition = addHocAreaDefinition;
262     }
263 
264     @Override
265     public void end(Appendable out) throws RenderException {
266 
267         try {
268             if (canRenderAreaScript()) {
269                 if (isInherit() && areaNode != null && areaDefinition.getInheritance() != null) {
270                     try {
271                         areaNode = new DefaultInheritanceContentDecorator(areaNode, areaDefinition.getInheritance()).wrapNode(areaNode);
272                     } catch (RepositoryException e) {
273                         throw new RuntimeRepositoryException(e);
274                     }
275                 }
276                 List<Node> listOfComponents = null;
277                 int numberOfComponents = 0;
278                 if (areaNode != null) {
279                     listOfComponents = NodeUtil.asList(NodeUtil.getNodes(areaNode, NodeTypes.Component.NAME));
280                     numberOfComponents = listOfComponents.size();
281                 }
282                 if (renderingEngine.getRenderEmptyAreas() || numberOfComponents > 0 || !(AreaDefinition.TYPE_LIST.equals(areaDefinition.getType()) || AreaDefinition.TYPE_SINGLE.equals(areaDefinition.getType()))) {
283 
284                     Map<String, Object> contextObjects = new HashMap<String, Object>();
285 
286                     List<ContentMap> components = new ArrayList<ContentMap>();
287 
288                     if (areaNode != null) {
289                         if (numberOfComponents > maxComponents) {
290                             listOfComponents = listOfComponents.subList(0, maxComponents);
291                             log.warn("The area {} have maximum number of components set to {}, but has got {} components. Exceeded components won't be added.", areaNode, maxComponents, numberOfComponents);
292                         }
293 
294                         for (Node node : listOfComponents) {
295                             components.add(new ContentMap(node));
296                         }
297                     }
298 
299                     if (AreaDefinition.TYPE_SINGLE.equals(type)) {
300                         if (components.size() > 1) {
301                             log.warn("Single area [{}]: expected one component node but found [{}].", areaNode, components.size());
302                         }
303                         if (components.size() >= 1) {
304                             contextObjects.put(ATTRIBUTE_COMPONENT, components.get(0));
305                         } else {
306                             contextObjects.put(ATTRIBUTE_COMPONENT, null);
307                         }
308                     } else {
309                         contextObjects.put(ATTRIBUTE_COMPONENTS, components);
310                     }
311 
312                     // We set fallbacks for when the area definition's properties area incomplete, providing the most
313                     // basic fallback values for rendering.
314                     // Note that we override the area definition only when necessary, otherwise proxying to the original
315                     final ConfiguredAreaDefinition override = new ConfiguredAreaDefinition(areaDefinition.getTemplateAvailability());
316                     if (areaDefinition.getTemplateScript() == null) {
317                         override.setRenderType(NoScriptRenderer.NO_SCRIPT_RENDERER);
318                     } else if (areaDefinition.getRenderType() == null) {
319                         override.setRenderType(templateDefinition.getRenderType());
320                     }
321                     if (areaDefinition.getI18nBasename() == null) {
322                         override.setI18nBasename(templateDefinition.getI18nBasename());
323                     }
324 
325                     final ConfiguredAreaDefinition mergedAreaDefinition = BeanMergerUtil.merge(override, areaDefinition);
326 
327                     WebContext webContext = MgnlContext.getWebContext();
328                     webContext.push(webContext.getRequest(), webContext.getResponse());
329                     setAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
330                     try {
331                         AppendableOnlyOutputProvider appendable = new AppendableOnlyOutputProvider(out);
332                         renderingEngine.render(areaNode, mergedAreaDefinition, contextObjects, appendable);
333                     } finally {
334                         webContext.pop();
335                         webContext.setPageContext(null);
336                         restoreAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
337                     }
338                 }
339             }
340             if (renderComments()) { // add condition into renderComments() method when adding extra condition to make sure it's in sync with adding comments in begin() method
341                 MarkupHelper helper = new MarkupHelper(out);
342                 helper.closeComment(CMS_AREA);
343             }
344         } catch (Exception e) {
345             throw new RenderException("Can't render area " + areaNode + " with name " + this.name, e);
346         }
347     }
348 
349     protected Node tryToCreateAreaNode(Node parentNode) throws RenderException {
350         Node area = null;
351         try {
352             if (parentNode.hasNode(name)) {
353                 area = parentNode.getNode(name);
354             } else {
355                 // autocreate and save area only if it's not optional
356                 if (!this.optional) {
357                     area = createNewAreaNode(parentNode);
358                 }
359             }
360         } catch (RepositoryException e) {
361             // yes, we now throw this consistently when area can't be created
362             log.error("Can't autocreate area '{}'.", area, e);
363         }
364         // at this stage we can be sure that the target area, unless optional, has been created.
365         if (area != null) {
366             // TODO fgrilli: what about other component types to be autogenerated (i.e. autogenerating an entire page)?
367             final AutoGenerationConfiguration autoGeneration = areaDefinition.getAutoGeneration();
368             if (autoGeneration != null && autoGeneration.getGeneratorClass() != null) {
369                 try {
370                     final String areaId = area.getIdentifier();
371                     final String workspaceName = area.getSession().getWorkspace().getName();
372                     MgnlContext.doInSystemContext(new MgnlContext.RepositoryOp() {
373                         @Override
374                         public void doExec() throws RepositoryException {
375                             Node areaNodeInSystemSession = NodeUtil.getNodeByIdentifier(workspaceName, areaId);
376                             try {
377                                 ((Generator<AutoGenerationConfiguration>) Components.newInstance(autoGeneration.getGeneratorClass(), areaNodeInSystemSession)).generate(autoGeneration);
378                             } catch (RenderException e) {
379                                 log.error("Can't render autogenerated area '{}'.", areaNodeInSystemSession);
380                             }
381                             return;
382                         }
383                     });
384                 } catch (RepositoryException e) {
385                     log.error("Can't autocreate area '{}'.", area, e);
386                 }
387             }
388         }
389         return area;
390     }
391 
392     protected AreaDefinition resolveAreaDefinition() {
393         if (areaDefinition != null) {
394             return areaDefinition;
395         }
396 
397         if (!StringUtils.isEmpty(name)) {
398             if (templateDefinition != null && templateDefinition.getAreas().containsKey(name)) {
399                 return templateDefinition.getAreas().get(name);
400             }
401         }
402         // happens if no area definition is passed or configured
403         // an ad-hoc area definition will be created
404         return null;
405     }
406 
407     protected TemplateDefinition resolveTemplateDefinition() throws RenderException {
408 
409         RenderableDefinition renderableDefinition = getRenderingContext().getRenderableDefinition();
410         RenderableDefinition variation = variationResolver.resolveVariation(renderableDefinition);
411         renderableDefinition = variation == null ? renderableDefinition : variation;
412 
413         if (renderableDefinition == null || renderableDefinition instanceof TemplateDefinition) {
414             return (TemplateDefinition) renderableDefinition;
415         }
416         throw new RenderException("Current RenderableDefinition [" + renderableDefinition + "] is not of type TemplateDefinition. Areas cannot be supported");
417     }
418 
419     /*
420      * An area script can be rendered when
421      * area is enabled
422      *
423      * AND
424      *
425      * If an area is optional:
426      *
427      * if not yet created the area bar has a create button and the script is
428      * - executed in the edit mode but the content object is null (otherwise we can't place the bar)
429      * - not executed otherwise (no place holder divs)
430      *
431      * If created, the bar has a remove button (other areas cannot be removed nor created)
432      *
433      * If an area is required:
434      *
435      * the area node gets created (always) the script is always executed.
436      */
437     private boolean canRenderAreaScript() {
438         if (!this.isAreaDefinitionEnabled) { // area script can be rendered only when area is enabled
439             return false;
440         }
441         if (this.areaNode != null) {
442             return true;
443         }
444         if (this.optional && this.getServer().isAdmin() && !MgnlContext.getAggregationState().isPreviewMode()) { // render script for optional areas when being in edit mode on author instance
445             return true;
446         }
447         return false;
448     }
449 
450     private String resolveDialog() {
451         return dialog != null ? dialog : areaDefinition != null ? areaDefinition.getDialog() : null;
452     }
453 
454     private String resolveType() {
455         return type != null ? type : areaDefinition != null && areaDefinition.getType() != null ? areaDefinition.getType() : AreaDefinition.DEFAULT_TYPE;
456     }
457 
458     private String resolveName() {
459         return name != null ? name : areaDefinition != null ? areaDefinition.getName() : null;
460     }
461 
462     private String resolveLabel() {
463         AreaDefinition i18nizedAreaDefinition = areaDefinition == null ? null : i18nize(areaDefinition);
464         String label = this.label != null ? this.label : (i18nizedAreaDefinition != null ? i18nizedAreaDefinition.getTitle() : null);
465 
466         //this was already translated with i18nizer, but translation could be missing. This is here just to keep compatibility with old i18n mechanism. Remove when we drop support of old i18n
467         final String i18nBasename = StringUtils.isNotEmpty(areaDefinition.getI18nBasename()) ? areaDefinition.getI18nBasename() : templateDefinition.getI18nBasename();
468         label = this.legacyTranslate(label, i18nBasename);
469 
470         if (label == null || isMessageKey(label)) {
471             label = StringUtils.capitalize(name);
472         }
473         return label;
474     }
475 
476     private Boolean resolveOptional() {
477         return optional != null ? optional : areaDefinition != null && areaDefinition.getOptional() != null ? areaDefinition.getOptional() : Boolean.FALSE;
478     }
479 
480     private Boolean resolveEditable() {
481         return editable != null ? editable : areaDefinition != null && areaDefinition.getEditable() != null ? areaDefinition.getEditable() : null;
482     }
483 
484     private Integer resolveMaximumOfComponents() {
485         return maxComponents != null ? maxComponents : areaDefinition != null && areaDefinition.getMaxComponents() != null ? areaDefinition.getMaxComponents() : Integer.MAX_VALUE;
486     }
487 
488     private Boolean resolveCreateAreaNode() {
489         return createAreaNode != null ? createAreaNode : areaDefinition != null && areaDefinition.getCreateAreaNode() != null ? areaDefinition.getCreateAreaNode() : Boolean.TRUE;
490     }
491 
492     private boolean isInheritanceEnabled() {
493         return areaDefinition != null && areaDefinition.getInheritance() != null && areaDefinition.getInheritance().isEnabled() != null && areaDefinition.getInheritance().isEnabled();
494     }
495 
496     private boolean isOptionalAreaCreated() {
497         return this.optional && this.areaNode != null;
498     }
499 
500     private boolean hasComponents(Node parent) throws RenderException {
501         try {
502             return NodeUtil.getNodes(parent, NodeTypes.Component.NAME).iterator().hasNext();
503         } catch (RepositoryException e) {
504             throw new RenderException(e);
505         }
506     }
507 
508     private int numberOfComponents(Node parent) throws RenderException {
509         try {
510             return NodeUtil.asList(NodeUtil.getNodes(parent, NodeTypes.Component.NAME)).size();
511         } catch (RepositoryException e) {
512             throw new RenderException(e);
513         }
514     }
515 
516     protected String resolveAvailableComponents() {
517         if (StringUtils.isNotEmpty(availableComponents)) {
518             return StringUtils.remove(availableComponents, " ");
519         }
520         if (areaDefinition != null && areaDefinition.getAvailableComponents().size() > 0) {
521             Iterator<ComponentAvailability> iterator = areaDefinition.getAvailableComponents().values().iterator();
522             List<String> componentIds = new ArrayList<String>();
523             final Collection<String> userRoles = MgnlContext.getUser().getAllRoles();
524             while (iterator.hasNext()) {
525                 ComponentAvailability availableComponent = iterator.next();
526                 if (availableComponent.isEnabled()) {
527                     // check roles
528                     final Collection<String> roles = availableComponent.getRoles();
529                     if (!roles.isEmpty()) {
530                         if (CollectionUtils.containsAny(userRoles, roles)) {
531                             componentIds.add(availableComponent.getId());
532                         }
533                     } else {
534                         componentIds.add(availableComponent.getId());
535                     }
536                 }
537             }
538             return StringUtils.join(componentIds, ',');
539         }
540         return "";
541     }
542 
543     private boolean shouldShowAddButton() throws RenderException {
544 
545         if (areaNode == null || type.equals(AreaDefinition.TYPE_NO_COMPONENT) || type.equals(AreaDefinition.TYPE_SINGLE) && hasComponents(areaNode) || numberOfComponents(areaNode) >= maxComponents) {
546             return false;
547         }
548         return true;
549     }
550 
551     private boolean shouldShowAddNewComponent() throws RenderException {
552 
553         if (areaNode == null || type.equals(AreaDefinition.TYPE_NO_COMPONENT) || type.equals(AreaDefinition.TYPE_SINGLE) && hasComponents(areaNode)) {
554             return false;
555         }
556         return true;
557     }
558 
559     public String getName() {
560         return name;
561     }
562 
563     public void setName(String name) {
564         this.name = name;
565     }
566 
567     public AreaDefinition getArea() {
568         return areaDefinition;
569     }
570 
571     public void setArea(AreaDefinition area) {
572         this.areaDefinition = area;
573     }
574 
575     public String getAvailableComponents() {
576         return availableComponents;
577     }
578 
579     public void setAvailableComponents(String availableComponents) {
580         this.availableComponents = availableComponents;
581     }
582 
583     public String getType() {
584         return type;
585     }
586 
587     public void setType(String type) {
588         this.type = type;
589     }
590 
591     public String getDialog() {
592         return dialog;
593     }
594 
595     public void setDialog(String dialog) {
596         this.dialog = dialog;
597     }
598 
599     public String getLabel() {
600         return label;
601     }
602 
603     public void setLabel(String label) {
604         this.label = label;
605     }
606 
607     public String getDescription() {
608         return description;
609     }
610 
611     public void setDescription(String description) {
612         this.description = description;
613     }
614 
615     public boolean isInherit() {
616         return inherit;
617     }
618 
619     public void setInherit(boolean inherit) {
620         this.inherit = inherit;
621     }
622 
623     public Boolean getEditable() {
624         return editable;
625     }
626 
627     public void setEditable(Boolean editable) {
628         this.editable = editable;
629     }
630 
631     public Map<String, Object> getContextAttributes() {
632         return contextAttributes;
633     }
634 
635     public void setContextAttributes(Map<String, Object> contextAttributes) {
636         this.contextAttributes = contextAttributes;
637     }
638 
639     public Integer getMaxComponents() {
640         return maxComponents;
641     }
642 
643     public void setMaxComponents(Integer maxComponents) {
644         this.maxComponents = maxComponents;
645     }
646 
647     public Boolean getCreateAreaNode() {
648         return createAreaNode;
649     }
650 
651     public void setCreateAreaNode(Boolean createAreaNode) {
652         this.createAreaNode = createAreaNode;
653     }
654 
655     @Override
656     protected boolean renderComments() {
657         return this.isAreaDefinitionEnabled && isAdmin() && !MgnlContext.getAggregationState().isPreviewMode() && hasPermission(this.areaNode);
658     }
659 }