View Javadoc
1   /**
2    * This file Copyright (c) 2011-2018 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                 if (parentNode.getDefinition().isProtected()) {
356                     log.debug("Not auto-creating area '{}', node is protected.");
357                     return null;
358                 } else if (!this.optional) {
359                     // autocreate and save area only if it's not optional
360                     area = createNewAreaNode(parentNode);
361                 }
362             }
363         } catch (RepositoryException e) {
364             // yes, we now throw this consistently when area can't be created
365             log.error("Can't autocreate area '{}'.", area, e);
366         }
367         // at this stage we can be sure that the target area, unless optional, has been created.
368         if (area != null) {
369             // TODO fgrilli: what about other component types to be autogenerated (i.e. autogenerating an entire page)?
370             final AutoGenerationConfiguration autoGeneration = areaDefinition.getAutoGeneration();
371             if (autoGeneration != null && autoGeneration.getGeneratorClass() != null) {
372                 try {
373                     final String areaId = area.getIdentifier();
374                     final String workspaceName = area.getSession().getWorkspace().getName();
375                     MgnlContext.doInSystemContext(new MgnlContext.RepositoryOp() {
376                         @Override
377                         public void doExec() throws RepositoryException {
378                             Node areaNodeInSystemSession = NodeUtil.getNodeByIdentifier(workspaceName, areaId);
379                             try {
380                                 ((Generator<AutoGenerationConfiguration>) Components.newInstance(autoGeneration.getGeneratorClass(), areaNodeInSystemSession)).generate(autoGeneration);
381                             } catch (RenderException e) {
382                                 log.error("Can't render autogenerated area '{}'.", areaNodeInSystemSession);
383                             }
384                             return;
385                         }
386                     });
387                 } catch (RepositoryException e) {
388                     log.error("Can't autocreate area '{}'.", area, e);
389                 }
390             }
391         }
392         return area;
393     }
394 
395     protected AreaDefinition resolveAreaDefinition() {
396         if (areaDefinition != null) {
397             return areaDefinition;
398         }
399 
400         if (!StringUtils.isEmpty(name)) {
401             if (templateDefinition != null && templateDefinition.getAreas().containsKey(name)) {
402                 return templateDefinition.getAreas().get(name);
403             }
404         }
405         // happens if no area definition is passed or configured
406         // an ad-hoc area definition will be created
407         return null;
408     }
409 
410     protected TemplateDefinition resolveTemplateDefinition() throws RenderException {
411 
412         RenderableDefinition renderableDefinition = getRenderingContext().getRenderableDefinition();
413         RenderableDefinition variation = variationResolver.resolveVariation(renderableDefinition);
414         renderableDefinition = variation == null ? renderableDefinition : variation;
415 
416         if (renderableDefinition == null || renderableDefinition instanceof TemplateDefinition) {
417             return (TemplateDefinition) renderableDefinition;
418         }
419         throw new RenderException("Current RenderableDefinition [" + renderableDefinition + "] is not of type TemplateDefinition. Areas cannot be supported");
420     }
421 
422     /*
423      * An area script can be rendered when
424      * area is enabled
425      *
426      * AND
427      *
428      * If an area is optional:
429      *
430      * if not yet created the area bar has a create button and the script is
431      * - executed in the edit mode but the content object is null (otherwise we can't place the bar)
432      * - not executed otherwise (no place holder divs)
433      *
434      * If created, the bar has a remove button (other areas cannot be removed nor created)
435      *
436      * If an area is required:
437      *
438      * the area node gets created (always) the script is always executed.
439      */
440     private boolean canRenderAreaScript() {
441         if (!this.isAreaDefinitionEnabled) { // area script can be rendered only when area is enabled
442             return false;
443         }
444         if (this.areaNode != null) {
445             return true;
446         }
447         if (this.optional && this.getServer().isAdmin() && !MgnlContext.getAggregationState().isPreviewMode()) { // render script for optional areas when being in edit mode on author instance
448             return true;
449         }
450         return false;
451     }
452 
453     private String resolveDialog() {
454         return dialog != null ? dialog : areaDefinition != null ? areaDefinition.getDialog() : null;
455     }
456 
457     private String resolveType() {
458         return type != null ? type : areaDefinition != null && areaDefinition.getType() != null ? areaDefinition.getType() : AreaDefinition.DEFAULT_TYPE;
459     }
460 
461     private String resolveName() {
462         return name != null ? name : areaDefinition != null ? areaDefinition.getName() : null;
463     }
464 
465     private String resolveLabel() {
466         AreaDefinition i18nizedAreaDefinition = areaDefinition == null ? null : i18nize(areaDefinition);
467         String label = this.label != null ? this.label : (i18nizedAreaDefinition != null ? i18nizedAreaDefinition.getTitle() : null);
468 
469         //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
470         final String i18nBasename = StringUtils.isNotEmpty(areaDefinition.getI18nBasename()) ? areaDefinition.getI18nBasename() : templateDefinition.getI18nBasename();
471         label = this.legacyTranslate(label, i18nBasename);
472 
473         if (label == null || isMessageKey(label)) {
474             label = StringUtils.capitalize(name);
475         }
476         return label;
477     }
478 
479     private Boolean resolveOptional() {
480         return optional != null ? optional : areaDefinition != null && areaDefinition.getOptional() != null ? areaDefinition.getOptional() : Boolean.FALSE;
481     }
482 
483     private Boolean resolveEditable() {
484         return editable != null ? editable : areaDefinition != null && areaDefinition.getEditable() != null ? areaDefinition.getEditable() : null;
485     }
486 
487     private Integer resolveMaximumOfComponents() {
488         return maxComponents != null ? maxComponents : areaDefinition != null && areaDefinition.getMaxComponents() != null ? areaDefinition.getMaxComponents() : Integer.MAX_VALUE;
489     }
490 
491     private Boolean resolveCreateAreaNode() {
492         return createAreaNode != null ? createAreaNode : areaDefinition != null && areaDefinition.getCreateAreaNode() != null ? areaDefinition.getCreateAreaNode() : Boolean.TRUE;
493     }
494 
495     private boolean isInheritanceEnabled() {
496         return areaDefinition != null && areaDefinition.getInheritance() != null && areaDefinition.getInheritance().isEnabled() != null && areaDefinition.getInheritance().isEnabled();
497     }
498 
499     private boolean isOptionalAreaCreated() {
500         return this.optional && this.areaNode != null;
501     }
502 
503     private boolean hasComponents(Node parent) throws RenderException {
504         try {
505             return NodeUtil.getNodes(parent, NodeTypes.Component.NAME).iterator().hasNext();
506         } catch (RepositoryException e) {
507             throw new RenderException(e);
508         }
509     }
510 
511     private int numberOfComponents(Node parent) throws RenderException {
512         try {
513             return NodeUtil.asList(NodeUtil.getNodes(parent, NodeTypes.Component.NAME)).size();
514         } catch (RepositoryException e) {
515             throw new RenderException(e);
516         }
517     }
518 
519     protected String resolveAvailableComponents() {
520         if (StringUtils.isNotEmpty(availableComponents)) {
521             return StringUtils.remove(availableComponents, " ");
522         }
523         if (areaDefinition != null && areaDefinition.getAvailableComponents().size() > 0) {
524             Iterator<ComponentAvailability> iterator = areaDefinition.getAvailableComponents().values().iterator();
525             List<String> componentIds = new ArrayList<String>();
526             final Collection<String> userRoles = MgnlContext.getUser().getAllRoles();
527             while (iterator.hasNext()) {
528                 ComponentAvailability availableComponent = iterator.next();
529                 if (availableComponent.isEnabled()) {
530                     // check roles
531                     final Collection<String> roles = availableComponent.getRoles();
532                     if (!roles.isEmpty()) {
533                         if (CollectionUtils.containsAny(userRoles, roles)) {
534                             componentIds.add(availableComponent.getId());
535                         }
536                     } else {
537                         componentIds.add(availableComponent.getId());
538                     }
539                 }
540             }
541             return StringUtils.join(componentIds, ',');
542         }
543         return "";
544     }
545 
546     private boolean shouldShowAddButton() throws RenderException {
547 
548         if (areaNode == null || type.equals(AreaDefinition.TYPE_NO_COMPONENT) || type.equals(AreaDefinition.TYPE_SINGLE) && hasComponents(areaNode) || numberOfComponents(areaNode) >= maxComponents) {
549             return false;
550         }
551         return true;
552     }
553 
554     private boolean shouldShowAddNewComponent() throws RenderException {
555 
556         if (areaNode == null || type.equals(AreaDefinition.TYPE_NO_COMPONENT) || type.equals(AreaDefinition.TYPE_SINGLE) && hasComponents(areaNode)) {
557             return false;
558         }
559         return true;
560     }
561 
562     public String getName() {
563         return name;
564     }
565 
566     public void setName(String name) {
567         this.name = name;
568     }
569 
570     public AreaDefinition getArea() {
571         return areaDefinition;
572     }
573 
574     public void setArea(AreaDefinition area) {
575         this.areaDefinition = area;
576     }
577 
578     public String getAvailableComponents() {
579         return availableComponents;
580     }
581 
582     public void setAvailableComponents(String availableComponents) {
583         this.availableComponents = availableComponents;
584     }
585 
586     public String getType() {
587         return type;
588     }
589 
590     public void setType(String type) {
591         this.type = type;
592     }
593 
594     public String getDialog() {
595         return dialog;
596     }
597 
598     public void setDialog(String dialog) {
599         this.dialog = dialog;
600     }
601 
602     public String getLabel() {
603         return label;
604     }
605 
606     public void setLabel(String label) {
607         this.label = label;
608     }
609 
610     public String getDescription() {
611         return description;
612     }
613 
614     public void setDescription(String description) {
615         this.description = description;
616     }
617 
618     public boolean isInherit() {
619         return inherit;
620     }
621 
622     public void setInherit(boolean inherit) {
623         this.inherit = inherit;
624     }
625 
626     public Boolean getEditable() {
627         return editable;
628     }
629 
630     public void setEditable(Boolean editable) {
631         this.editable = editable;
632     }
633 
634     public Map<String, Object> getContextAttributes() {
635         return contextAttributes;
636     }
637 
638     public void setContextAttributes(Map<String, Object> contextAttributes) {
639         this.contextAttributes = contextAttributes;
640     }
641 
642     public Integer getMaxComponents() {
643         return maxComponents;
644     }
645 
646     public void setMaxComponents(Integer maxComponents) {
647         this.maxComponents = maxComponents;
648     }
649 
650     public Boolean getCreateAreaNode() {
651         return createAreaNode;
652     }
653 
654     public void setCreateAreaNode(Boolean createAreaNode) {
655         this.createAreaNode = createAreaNode;
656     }
657 
658     @Override
659     protected boolean renderComments() {
660         return this.isAreaDefinitionEnabled && isAdmin() && !MgnlContext.getAggregationState().isPreviewMode() && hasPermission(this.areaNode);
661     }
662 }