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