001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.containerpage.CmsContainerpageService;
032import org.opencms.ade.containerpage.CmsDetailOnlyContainerUtil;
033import org.opencms.ade.containerpage.CmsElementUtil;
034import org.opencms.ade.containerpage.shared.CmsContainer;
035import org.opencms.ade.containerpage.shared.CmsContainerElement;
036import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
037import org.opencms.file.CmsGroup;
038import org.opencms.file.CmsObject;
039import org.opencms.file.CmsResource;
040import org.opencms.file.CmsUser;
041import org.opencms.file.CmsVfsResourceNotFoundException;
042import org.opencms.flex.CmsFlexController;
043import org.opencms.gwt.shared.CmsGwtConstants;
044import org.opencms.gwt.shared.CmsTemplateContextInfo;
045import org.opencms.i18n.CmsEncoder;
046import org.opencms.jsp.CmsJspTagAddParams.ParamState;
047import org.opencms.jsp.util.CmsJspStandardContextBean;
048import org.opencms.jsp.util.CmsJspStandardContextBean.CmsContainerElementWrapper;
049import org.opencms.loader.CmsLoaderException;
050import org.opencms.loader.CmsTemplateContext;
051import org.opencms.loader.CmsTemplateContextManager;
052import org.opencms.loader.I_CmsTemplateContextProvider;
053import org.opencms.main.CmsException;
054import org.opencms.main.CmsIllegalStateException;
055import org.opencms.main.CmsLog;
056import org.opencms.main.OpenCms;
057import org.opencms.security.CmsPermissionViolationException;
058import org.opencms.security.CmsRole;
059import org.opencms.util.CmsRequestUtil;
060import org.opencms.util.CmsStringUtil;
061import org.opencms.util.CmsUUID;
062import org.opencms.xml.containerpage.CmsADESessionCache;
063import org.opencms.xml.containerpage.CmsContainerBean;
064import org.opencms.xml.containerpage.CmsContainerElementBean;
065import org.opencms.xml.containerpage.CmsContainerPageBean;
066import org.opencms.xml.containerpage.CmsFormatterConfiguration;
067import org.opencms.xml.containerpage.CmsGroupContainerBean;
068import org.opencms.xml.containerpage.CmsXmlContainerPage;
069import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
070import org.opencms.xml.containerpage.CmsXmlGroupContainer;
071import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory;
072import org.opencms.xml.containerpage.CmsXmlInheritGroupContainerHandler;
073import org.opencms.xml.containerpage.I_CmsFormatterBean;
074import org.opencms.xml.templatemapper.CmsTemplateMapper;
075
076import java.io.IOException;
077import java.util.ArrayList;
078import java.util.Collections;
079import java.util.HashMap;
080import java.util.List;
081import java.util.Locale;
082import java.util.Map;
083import java.util.Map.Entry;
084
085import javax.servlet.ServletRequest;
086import javax.servlet.ServletResponse;
087import javax.servlet.http.HttpServletRequest;
088import javax.servlet.http.HttpServletResponse;
089import javax.servlet.jsp.JspException;
090import javax.servlet.jsp.tagext.BodyContent;
091import javax.servlet.jsp.tagext.BodyTagSupport;
092import javax.servlet.jsp.tagext.TryCatchFinally;
093
094import org.apache.commons.lang3.ClassUtils;
095import org.apache.commons.logging.Log;
096
097/**
098 * Provides access to the page container elements.<p>
099 *
100 * @since 8.0
101 */
102public class CmsJspTagContainer extends BodyTagSupport implements TryCatchFinally, I_CmsJspTagParamParent {
103
104    /** Default number of max elements in the container in case no value has been set. */
105    public static final String DEFAULT_MAX_ELEMENTS = "100";
106
107    /** The detail function container name. */
108    public static final String DETAIL_FUNCTION_CONTAINER_NAME = "FunctionDefault";
109
110    /** HTML used for invisible dummy elements. */
111    public static final String DUMMY_ELEMENT = "<div class='"
112        + CmsTemplateContextInfo.DUMMY_ELEMENT_MARKER
113        + "' style='display: none !important;'></div>";
114
115    /** The default tag name constant. */
116    private static final String DEFAULT_TAG_NAME = "div";
117
118    /** The log object for this class. */
119    private static final Log LOG = CmsLog.getLog(CmsJspTagContainer.class);
120
121    /** Serial version UID required for safe serialization. */
122    private static final long serialVersionUID = -1228397990961282556L;
123
124    /** The evaluated body content if available. */
125    private String m_bodyContent;
126
127    /** If false, formatters are always included in non-cacheable mode, otherwise they are included in cacheable mode in the Online project only. */
128    private boolean m_cacheable = true;
129
130    /** States if this container should only be displayed on detail pages. */
131    private boolean m_detailOnly;
132
133    /** The detail-view attribute value. */
134    private boolean m_detailView;
135
136    /** The editable by tag attribute. A comma separated list of OpenCms principals. */
137    private String m_editableBy;
138
139    /** Indicating that the container page editor is active for the current request. */
140    private boolean m_editableRequest;
141
142    /** Indicates this container is nested within a model group, only set for editable requests. */
143    private boolean m_hasModelGroupAncestor;
144
145    /** The maxElements attribute value. */
146    private String m_maxElements;
147
148    /** The name attribute value. */
149    private String m_name;
150
151    /**
152     * The container name prefix to use for nested container names.
153     * If empty the element instance id of the parent element will be used.
154     **/
155    private String m_namePrefix;
156
157    /** The optional container parameter. */
158    private String m_param;
159
160    /** The parameter state. */
161    private CmsJspTagAddParams.ParamState m_paramState;
162
163    /** The parent container. */
164    private CmsContainerBean m_parentContainer;
165
166    /** The parent element to this container. */
167    private CmsContainerElementBean m_parentElement;
168
169    /** The container setting presets. */
170    private HashMap<String, String> m_settingPresets;
171
172    /** The tag attribute value. */
173    private String m_tag;
174
175    /** The class attribute value. */
176    private String m_tagClass;
177
178    /** The type attribute value. */
179    private String m_type;
180
181    /** The container width as a string. */
182    private String m_width;
183
184    /**
185     * Ensures the appropriate formatter configuration ID is set in the element settings.<p>
186     *
187     * @param cms the cms context
188     * @param element the element bean
189     * @param adeConfig the ADE configuration data
190     * @param containerName the container name
191     * @param containerType the container type
192     * @param containerWidth the container width
193     *
194     * @return the formatter configuration bean, may be <code>null</code> if no formatter available or a schema formatter is used
195     */
196    public static I_CmsFormatterBean ensureValidFormatterSettings(
197        CmsObject cms,
198        CmsContainerElementBean element,
199        CmsADEConfigData adeConfig,
200        String containerName,
201        String containerType,
202        int containerWidth) {
203
204        I_CmsFormatterBean formatterBean = getFormatterConfigurationForElement(
205            cms,
206            element,
207            adeConfig,
208            containerName,
209            containerType,
210            containerWidth);
211        String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(containerName);
212        if (formatterBean != null) {
213            String keyOrId = formatterBean.getKeyOrId();
214            if (keyOrId == null) {
215                keyOrId = CmsFormatterConfig.SCHEMA_FORMATTER_ID + formatterBean.getJspStructureId().toString();
216            }
217            element.getSettings().put(settingsKey, keyOrId);
218            element.setFormatterId(formatterBean.getJspStructureId());
219        }
220        return formatterBean;
221    }
222
223    /**
224     * Returns the formatter configuration for the given element.<p>
225     *
226     * @param cms the cms context
227     * @param element the element bean
228     * @param adeConfig the ADE configuration
229     * @param containerName the container name
230     * @param containerType the container type
231     * @param containerWidth the container width
232     *
233     * @return the formatter configuration
234     */
235    public static I_CmsFormatterBean getFormatterConfigurationForElement(
236        CmsObject cms,
237        CmsContainerElementBean element,
238        CmsADEConfigData adeConfig,
239        String containerName,
240        String containerType,
241        int containerWidth) {
242
243        I_CmsFormatterBean formatterBean = null;
244        String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(containerName);
245        String formatterSetting = element.getSettings().get(settingsKey);
246        CmsFormatterConfiguration formatterConfig = null;
247        if (formatterSetting != null) {
248            formatterConfig = adeConfig.getFormatters(cms, element.getResource());
249            // getFormattersForKey also works for the schema_formaterXXXXXX setting values
250            List<I_CmsFormatterBean> candidates = formatterConfig.getFormattersForKey(formatterSetting);
251            if (candidates.size() > 0) {
252                formatterBean = candidates.get(0);
253            } else {
254                formatterBean = adeConfig.findFormatter(formatterSetting);
255            }
256        }
257
258        if ((formatterBean == null) && (element.getFormatterId() != null) && !element.getFormatterId().isNullUUID()) {
259            if (formatterConfig == null) {
260                formatterConfig = adeConfig.getFormatters(cms, element.getResource());
261            }
262
263            for (I_CmsFormatterBean formatter : adeConfig.getFormatters(
264                cms,
265                element.getResource()).getAllMatchingFormatters(containerType, containerWidth)) {
266
267                if (element.getFormatterId().equals(formatter.getJspStructureId())) {
268                    String formatterConfigId = formatter.getId();
269                    if (formatterConfigId == null) {
270                        formatterConfigId = CmsFormatterConfig.SCHEMA_FORMATTER_ID
271                            + element.getFormatterId().toString();
272                    }
273                    formatterBean = formatter;
274                    break;
275                }
276            }
277        }
278
279        if (formatterBean == null) {
280
281            formatterBean = adeConfig.getFormatters(cms, element.getResource()).getDefaultFormatter(
282                containerType,
283                containerWidth);
284        }
285        return formatterBean;
286    }
287
288    /**
289     * Returns the element group elements.<p>
290     *
291     * @param cms the current cms context
292     * @param element group element
293     * @param req the servlet request
294     * @param containerType the container type
295     *
296     * @return the elements of this group
297     *
298     * @throws CmsException if something goes wrong
299     */
300    public static List<CmsContainerElementBean> getGroupContainerElements(
301        CmsObject cms,
302        CmsContainerElementBean element,
303        ServletRequest req,
304        String containerType)
305    throws CmsException {
306
307        List<CmsContainerElementBean> subElements;
308        CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(cms, element.getResource(), req);
309        CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(cms);
310        groupContainer = CmsTemplateMapper.get(req).transformGroupContainer(
311            cms,
312            groupContainer,
313            xmlGroupContainer.getFile().getRootPath());
314        if (!CmsElementUtil.checkGroupAllowed(containerType, groupContainer)) {
315            LOG.warn(
316                new CmsIllegalStateException(
317                    Messages.get().container(
318                        Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3,
319                        element.getResource().getRootPath(),
320                        OpenCms.getResourceManager().getResourceType(element.getResource()).getTypeName(),
321                        containerType)));
322            return Collections.emptyList();
323        }
324        subElements = groupContainer.getElements();
325        return subElements;
326    }
327
328    /**
329     * Reads elements from an inherited container.<p>
330     *
331     * @param cms the current CMS context
332     * @param element the element which references the inherited container
333     *
334     * @return the container elements
335     */
336
337    public static List<CmsContainerElementBean> getInheritedContainerElements(
338        CmsObject cms,
339        CmsContainerElementBean element) {
340
341        CmsResource resource = element.getResource();
342        return CmsXmlInheritGroupContainerHandler.loadInheritContainerElements(cms, resource);
343    }
344
345    /**
346     * Returns the prefixed nested container name.<p>
347     * This will be either {parentInstanceId}-{name} or {namePrefix}-{name} or in case namePrefix equals 'none' {name} only.<p>
348     *
349     * @param name the container name
350     * @param parentIstanceId the parent instance id
351     * @param namePrefix the name prefix attribute
352     *
353     * @return the nested container name
354     */
355    public static String getNestedContainerName(String name, String parentIstanceId, String namePrefix) {
356
357        String prefix;
358        if (CmsStringUtil.isEmptyOrWhitespaceOnly(namePrefix)) {
359            prefix = parentIstanceId + "-";
360        } else if ("none".equals(namePrefix)) {
361            prefix = "";
362        } else {
363            prefix = namePrefix + "-";
364        }
365        return prefix + name;
366    }
367
368    /**
369     * Creates the closing tag for the container.<p>
370     *
371     * @param tagName the tag name
372     *
373     * @return the closing tag
374     */
375    protected static String getTagClose(String tagName) {
376
377        return "</" + tagName + ">";
378    }
379
380    /**
381     * Creates the opening tag for the container assigning the appropriate id and class attributes.<p>
382     *
383     * @param tagName the tag name
384     * @param containerName the container name used as id attribute value
385     * @param tagClass the tag class attribute value
386     * @param nested true if this is a nested container
387     * @param online true if we are in the online project
388     * @param containerData the container data
389     *
390     * @return the opening tag
391     */
392    protected static String getTagOpen(
393        String tagName,
394        String containerName,
395        String tagClass,
396        boolean nested,
397        boolean online,
398        String containerData) {
399
400        StringBuffer buffer = new StringBuffer(32);
401        buffer.append("<").append(tagName).append(" ");
402        if (online && nested) {
403            // omit generated ids when online
404        } else {
405            buffer.append(" id=\"").append(containerName).append("\" ");
406        }
407        if (containerData != null) {
408            buffer.append(" " + CmsGwtConstants.ATTR_DATA_CONTAINER + "=\"").append(containerData).append("\" ");
409            // set the marker CSS class
410            tagClass = tagClass == null
411            ? CmsContainerElement.CLASS_CONTAINER
412            : tagClass + " " + CmsContainerElement.CLASS_CONTAINER;
413        }
414        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(tagClass)) {
415            buffer.append("class=\"").append(tagClass).append("\" ");
416        }
417        buffer.append(">");
418        return buffer.toString();
419    }
420
421    /**
422     * @see org.opencms.jsp.I_CmsJspTagParamParent#addParameter(java.lang.String, java.lang.String)
423     */
424    public void addParameter(String name, String value) {
425
426        if (m_paramState != null) {
427            m_paramState.addParameter(name, value);
428        }
429    }
430
431    /**
432     * @see javax.servlet.jsp.tagext.BodyTagSupport#doAfterBody()
433     */
434    @SuppressWarnings("resource")
435    @Override
436    public int doAfterBody() {
437
438        // store the evaluated body content for later use
439        BodyContent bc = getBodyContent();
440        if (bc != null) {
441            m_bodyContent = bc.getString();
442            try {
443                bc.clear();
444            } catch (IOException e) {
445                LOG.error(e.getLocalizedMessage(), e);
446            }
447        }
448        return SKIP_BODY;
449    }
450
451    /**
452     * @see javax.servlet.jsp.tagext.TryCatchFinally#doCatch(java.lang.Throwable)
453     */
454    public void doCatch(Throwable t) throws Throwable {
455
456        throw t;
457    }
458
459    /**
460     * @see javax.servlet.jsp.tagext.TagSupport#doEndTag()
461     */
462    @Override
463    public int doEndTag() throws JspException {
464
465        ServletRequest req = pageContext.getRequest();
466        // This will always be true if the page is called through OpenCms
467        if (CmsFlexController.isCmsRequest(req)) {
468
469            try {
470                CmsFlexController controller = CmsFlexController.getController(req);
471                CmsObject cms = controller.getCmsObject();
472                String requestUri = cms.getRequestContext().getUri();
473                Locale locale = cms.getRequestContext().getLocale();
474                CmsJspStandardContextBean standardContext = CmsJspStandardContextBean.getInstance(req);
475                standardContext.initPage();
476                m_editableRequest = standardContext.getIsEditMode();
477                m_parentElement = standardContext.getElement();
478                m_parentContainer = standardContext.getContainer();
479                m_hasModelGroupAncestor = m_editableRequest ? hasModelGroupAncestor(standardContext) : false;
480                CmsContainerPageBean containerPage = standardContext.getPage();
481                CmsResource detailContent = standardContext.getDetailContent();
482                CmsResource detailFunctionPage = standardContext.getDetailFunctionPage();
483                // get the container
484                CmsContainerBean container = null;
485                boolean detailOnly = m_detailOnly || ((m_parentContainer != null) && m_parentContainer.isDetailOnly());
486                if (detailOnly) {
487                    if (detailContent == null) {
488                        // this is no detail page, so the detail only container will not be rendered at all
489                        resetState();
490                        return EVAL_PAGE;
491                    } else {
492                        String pageRootPath = cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri());
493                        CmsContainerPageBean detailOnlyPage = CmsDetailOnlyContainerUtil.getDetailOnlyPage(
494                            cms,
495                            req,
496                            pageRootPath);
497                        if (detailOnlyPage != null) {
498                            container = detailOnlyPage.getContainers().get(getName());
499                        }
500                        if ((container == null) && m_editableRequest && (containerPage != null)) {
501                            // this is for the case where the current container is the nested container of a model group which the user is dragging into a detail container
502                            container = containerPage.getContainers().get(getName());
503                        }
504                    }
505                } else if (containerPage != null) {
506                    container = containerPage.getContainers().get(getName());
507                }
508                // get the maximal number of elements
509                int maxElements = getMaxElements(requestUri);
510                if (container == null) {
511                    container = new CmsContainerBean(
512                        getName(),
513                        getType(),
514                        m_parentElement != null ? m_parentElement.getInstanceId() : null,
515                        (m_parentContainer == null) || (m_detailOnly && !m_parentContainer.isDetailOnly()),
516                        maxElements,
517                        Collections.<CmsContainerElementBean> emptyList());
518                } else if ((m_parentElement != null)
519                    && !m_detailOnly //ignore parent information for detail only containers to render content on different detail pages.
520                    && !m_parentElement.getInstanceId().equals(container.getParentInstanceId())) {
521                    // the container parent instance id does not match the parent element instance id, skip rendering to avoid recursion
522                    LOG.error(
523                        new CmsIllegalStateException(
524                            Messages.get().container(
525                                Messages.ERR_INVALID_CONTAINER_PARENT_2,
526                                getName(),
527                                m_parentElement.getInstanceId())));
528                    resetState();
529                    return EVAL_PAGE;
530                }
531                // set the parameter
532                container.setParam(getParam());
533                // set the detail only flag
534                container.setDetailOnly(detailOnly);
535                boolean isUsedAsDetailView = false;
536                if (m_detailView && ((detailContent != null) || (detailFunctionPage != null))) {
537                    isUsedAsDetailView = true;
538                }
539                // create tag for container
540                String tagName = CmsStringUtil.isEmptyOrWhitespaceOnly(getTag()) ? DEFAULT_TAG_NAME : getTag();
541                pageContext.getOut().print(
542                    getTagOpen(
543                        tagName,
544                        getName(),
545                        getTagClass(),
546                        isNested(),
547                        !m_editableRequest,
548                        m_editableRequest ? getContainerData(cms, maxElements, isUsedAsDetailView, detailOnly) : null));
549
550                standardContext.setContainer(container);
551                // validate the type
552                if (!getType().equals(container.getType())) {
553                    container.setType(getType());
554                    LOG.warn(
555                        new CmsIllegalStateException(
556                            Messages.get().container(
557                                Messages.LOG_WRONG_CONTAINER_TYPE_4,
558                                new Object[] {requestUri, locale, getName(), getType()})));
559                }
560
561                // update the cache
562                container.setMaxElements(maxElements);
563                container.setWidth("" + getContainerWidth());
564                List<CmsContainerElementBean> allElements = new ArrayList<CmsContainerElementBean>();
565                CmsContainerElementBean detailElement = null;
566                if (isUsedAsDetailView) {
567                    if (detailContent != null) {
568                        detailElement = generateDetailViewElement(req, cms, detailContent, container);
569                    } else {
570                        detailElement = getDetailFunctionElement(cms, detailFunctionPage, req);
571                    }
572                }
573                if (detailElement != null) {
574                    allElements.add(detailElement);
575                } else {
576                    allElements.addAll(container.getElements());
577                }
578                // iterate over elements to render
579                int numRenderedElements = 0;
580                boolean first = true;
581                for (CmsContainerElementBean elementBean : allElements) {
582                    // in case of rendering a detail container on a detail page,
583                    // the first element may be used to provide settings for the detail content
584                    // this element will not be rendered, in case the detail page is not actually used to render detail content
585                    boolean skipDetailTemplateElement = false;
586                    try {
587                        skipDetailTemplateElement = first
588                            && !m_editableRequest
589                            && m_detailView
590                            && (detailElement == null)
591                            && OpenCms.getADEManager().isDetailPage(cms, standardContext.getPageResource())
592                            && OpenCms.getADEManager().getDetailPages(cms, elementBean.getTypeName()).contains(
593                                CmsResource.getFolderPath(standardContext.getPageResource().getRootPath()));
594                    } catch (Exception e) {
595                        LOG.error(e.getLocalizedMessage(), e);
596                    }
597                    first = false;
598                    if (!skipDetailTemplateElement) {
599                        try {
600                            boolean rendered = renderContainerElement(
601                                (HttpServletRequest)req,
602                                cms,
603                                standardContext,
604                                elementBean,
605                                locale,
606                                numRenderedElements >= maxElements);
607                            if (rendered) {
608                                numRenderedElements += 1;
609                            }
610                        } catch (Exception e) {
611                            if (LOG.isErrorEnabled()) {
612                                LOG.error(e.getLocalizedMessage(), e);
613                            }
614                        }
615                    }
616                }
617                if ((numRenderedElements == 0) && (m_bodyContent != null) && CmsJspTagEditable.isEditableRequest(req)) {
618                    // the container is empty, print the evaluated body content
619                    pageContext.getOut().print(m_bodyContent);
620                }
621                // close tag for container
622                pageContext.getOut().print(getTagClose(tagName));
623            } catch (Exception ex) {
624                if (LOG.isErrorEnabled()) {
625                    LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "container"), ex);
626                }
627                throw new javax.servlet.jsp.JspException(ex);
628            }
629        }
630
631        resetState();
632        return super.doEndTag();
633    }
634
635    /**
636     * @see javax.servlet.jsp.tagext.TryCatchFinally#doFinally()
637     */
638    public void doFinally() {
639
640        if (m_paramState != null) {
641            m_paramState.undoChanges();
642            m_paramState = null;
643        }
644    }
645
646    /**
647     * Internal action method.<p>
648     *
649     * @return EVAL_BODY_BUFFERED
650     *
651     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
652     */
653    @Override
654    public int doStartTag() {
655
656        if (CmsFlexController.isCmsRequest(pageContext.getRequest())) {
657            m_paramState = new ParamState(
658                CmsFlexController.getController(pageContext.getRequest()).getCurrentRequest());
659            m_paramState.init();
660        }
661        return EVAL_BODY_BUFFERED;
662    }
663
664    /**
665     * Returns the boolean value if this container is target of detail views.<p>
666     *
667     * @return <code>true</code> or <code>false</code>
668     */
669    public String getDetailview() {
670
671        return String.valueOf(m_detailView);
672    }
673
674    /**
675     * Returns the editable by tag attribute.<p>
676     *
677     * @return the editable by tag attribute
678     */
679    public String getEditableby() {
680
681        return m_editableBy;
682    }
683
684    /**
685     * Returns the maxElements attribute value.<p>
686     *
687     * @return the maxElements attribute value
688     */
689    public String getMaxElements() {
690
691        return CmsStringUtil.isEmptyOrWhitespaceOnly(m_maxElements) ? DEFAULT_MAX_ELEMENTS : m_maxElements;
692    }
693
694    /**
695     * Returns the container name, in case of nested containers with a prefix to guaranty uniqueness.<p>
696     *
697     * @return String the container name
698     */
699    public String getName() {
700
701        if (isNested()) {
702            return getNestedContainerName(m_name, m_parentElement.getInstanceId(), m_namePrefix);
703        }
704        return m_name;
705    }
706
707    /**
708     * Returns the name prefix.<p>
709     *
710     * @return the namePrefix
711     */
712    public String getNameprefix() {
713
714        return m_namePrefix;
715    }
716
717    /**
718     * Returns the (optional) container parameter.<p>
719     *
720     * This is useful for a dynamically generated nested container,
721     * to pass information to the formatter used inside that container.
722     *
723     * If no parameters have been set, this will return <code>null</code>
724     *
725     * @return the (optional) container parameter
726     */
727    public String getParam() {
728
729        return m_param;
730    }
731
732    /**
733     * Returns the tag attribute.<p>
734     *
735     * @return the tag attribute
736     */
737    public String getTag() {
738
739        return m_tag;
740    }
741
742    /**
743     * Returns the tag class attribute.<p>
744     *
745     * @return the tag class attribute
746     */
747    public String getTagClass() {
748
749        return m_tagClass;
750    }
751
752    /**
753     * Returns the type attribute value.<p>
754     *
755     * If the container type has not been set, the name is substituted as type.<p>
756     *
757     * @return the type attribute value
758     */
759    public String getType() {
760
761        return CmsStringUtil.isEmptyOrWhitespaceOnly(m_type) ? getName() : m_type;
762    }
763
764    /**
765     * Returns the container width as a string.<p>
766     *
767     * @return the container width as a string
768     */
769    public String getWidth() {
770
771        return m_width;
772    }
773
774    /**
775     * Sets the 'cacheable' mode for included formatters.
776     *
777     * <p>If this is set to false, formatters will never be included in cacheable mode, otherwise they will
778     * only be included in cacheable mode in the Online project.
779     *
780     * @param cacheable the cacheable mode (true or false)
781     */
782    public void setCacheable(String cacheable) {
783
784        m_cacheable = Boolean.parseBoolean(cacheable);
785    }
786
787    /**
788     * Sets if this container should only be displayed on detail pages.<p>
789     *
790     * @param detailOnly if this container should only be displayed on detail pages
791     */
792    public void setDetailonly(String detailOnly) {
793
794        m_detailOnly = Boolean.parseBoolean(detailOnly);
795    }
796
797    /**
798     * Sets if the current container is target of detail views.<p>
799     *
800     * @param detailView <code>true</code> or <code>false</code>
801     */
802    public void setDetailview(String detailView) {
803
804        m_detailView = Boolean.parseBoolean(detailView);
805    }
806
807    /**
808     * Sets the editable by tag attribute.<p>
809     *
810     * @param editableBy the editable by tag attribute to set
811     */
812    public void setEditableby(String editableBy) {
813
814        m_editableBy = editableBy;
815    }
816
817    /**
818     * Sets the maxElements attribute value.<p>
819     *
820     * @param maxElements the maxElements value to set
821     */
822    public void setMaxElements(String maxElements) {
823
824        m_maxElements = maxElements;
825    }
826
827    /**
828     * Sets the name attribute value.<p>
829     *
830     * @param name the name value to set
831     */
832    public void setName(String name) {
833
834        m_name = name;
835    }
836
837    /**
838     * Sets the name prefix.<p>
839     *
840     * @param namePrefix the name prefix to set
841     */
842    public void setNameprefix(String namePrefix) {
843
844        m_namePrefix = namePrefix;
845    }
846
847    /**
848     * Sets the container parameter.<p>
849     *
850     * This is useful for a dynamically generated nested container,
851     * to pass information to the formatter used inside that container.
852     *
853     * @param param the parameter String to set
854     */
855    public void setParam(String param) {
856
857        m_param = param;
858    }
859
860    /**
861     * Sets the setting presets.<p>
862     *
863     * @param presets a map with string keys and values, or null
864     */
865    @SuppressWarnings("unchecked")
866    public void setSettings(Object presets) {
867
868        if (presets == null) {
869            m_settingPresets = null;
870        } else if (!(presets instanceof Map)) {
871            throw new IllegalArgumentException(
872                "cms:container -- value of 'settings' attribute  should be a map, but is "
873                    + ClassUtils.getCanonicalName(presets));
874        } else {
875            m_settingPresets = new HashMap<>((Map<String, String>)presets);
876        }
877    }
878
879    /**
880     * Sets the tag attribute.<p>
881     *
882     * @param tag the createTag to set
883     */
884    public void setTag(String tag) {
885
886        m_tag = tag;
887    }
888
889    /**
890     * Sets the tag class attribute.<p>
891     *
892     * @param tagClass the tag class attribute to set
893     */
894    public void setTagClass(String tagClass) {
895
896        m_tagClass = tagClass;
897    }
898
899    /**
900     * Sets the type attribute value.<p>
901     *
902     * @param type the type value to set
903     */
904    public void setType(String type) {
905
906        m_type = type;
907    }
908
909    /**
910     * Sets the container width as a string.<p>
911     *
912     * @param width the container width as a string
913     */
914    public void setWidth(String width) {
915
916        m_width = width;
917    }
918
919    /**
920     * Returns the serialized data of the given container.<p>
921     *
922     * @param cms the cms context
923     * @param maxElements the maximum number of elements allowed within this container
924     * @param isDetailView <code>true</code> if this container is currently being used for the detail view
925     * @param isDetailOnly <code>true</code> if this is a detail only container
926     *
927     * @return the serialized container data
928     */
929    protected String getContainerData(CmsObject cms, int maxElements, boolean isDetailView, boolean isDetailOnly) {
930
931        int width = -1;
932        try {
933            if (getWidth() != null) {
934                width = Integer.parseInt(getWidth());
935            }
936        } catch (NumberFormatException e) {
937            //ignore; set width to -1
938            LOG.debug("Error parsing container width.", e);
939        }
940        CmsContainer cont = new CmsContainer(
941            getName(),
942            getType(),
943            m_bodyContent,
944            width,
945            maxElements,
946            m_detailView,
947            isDetailView,
948            !m_hasModelGroupAncestor && isEditable(cms),
949            null,
950            m_parentContainer != null ? m_parentContainer.getName() : null,
951            m_parentElement != null ? m_parentElement.getInstanceId() : null,
952            m_settingPresets);
953        cont.setDetailOnly(isDetailOnly);
954        String result = "";
955        try {
956            result = CmsContainerpageService.getSerializedContainerInfo(cont);
957        } catch (Exception e) {
958            LOG.error(e.getLocalizedMessage(), e);
959        }
960
961        return result;
962    }
963
964    /**
965     * Returns if the container is editable by the current user.<p>
966     *
967     * @param cms the cms context
968     *
969     * @return <code>true</code> if the container is editable by the current user
970     */
971    protected boolean isEditable(CmsObject cms) {
972
973        boolean result = false;
974        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_editableBy)) {
975            String[] principals = m_editableBy.split(",");
976            List<CmsGroup> groups = null;
977            for (int i = 0; i < principals.length; i++) {
978                String key = principals[i];
979                // get the principal name from the principal String
980                String principal = key.substring(key.indexOf('.') + 1, key.length());
981
982                if (CmsGroup.hasPrefix(key)) {
983                    // read the group
984                    principal = OpenCms.getImportExportManager().translateGroup(principal);
985                    try {
986                        CmsGroup group = cms.readGroup(principal);
987                        if (groups == null) {
988                            try {
989                                groups = cms.getGroupsOfUser(cms.getRequestContext().getCurrentUser().getName(), false);
990                            } catch (Exception ex) {
991                                if (LOG.isErrorEnabled()) {
992                                    LOG.error(ex.getLocalizedMessage(), ex);
993                                }
994                                groups = Collections.emptyList();
995                            }
996                        }
997                        result = groups.contains(group);
998                    } catch (CmsException e) {
999                        if (LOG.isErrorEnabled()) {
1000                            LOG.error(e.getLocalizedMessage(), e);
1001                        }
1002                    }
1003                } else if (CmsUser.hasPrefix(key)) {
1004                    // read the user
1005                    principal = OpenCms.getImportExportManager().translateUser(principal);
1006                    try {
1007                        result = cms.getRequestContext().getCurrentUser().equals(cms.readUser(principal));
1008                    } catch (CmsException e) {
1009                        if (LOG.isErrorEnabled()) {
1010                            LOG.error(e.getLocalizedMessage(), e);
1011                        }
1012                    }
1013                } else if (CmsRole.hasPrefix(key)) {
1014                    // read the role with role name
1015                    CmsRole role = CmsRole.valueOfRoleName(principal);
1016                    if (role == null) {
1017                        // try to read the role in the old fashion with group name
1018                        role = CmsRole.valueOfGroupName(principal);
1019                    }
1020                    if (role != null) {
1021                        result = OpenCms.getRoleManager().hasRole(
1022                            cms,
1023                            role.forOrgUnit(cms.getRequestContext().getCurrentUser().getOuFqn()));
1024                    }
1025                }
1026                if (result) {
1027                    break;
1028                }
1029            }
1030        } else {
1031            result = OpenCms.getRoleManager().hasRole(cms, CmsRole.ELEMENT_AUTHOR);
1032        }
1033        return result;
1034    }
1035
1036    /**
1037     * Returns true if this is a nested container.<p>
1038     *
1039     * @return true if this is a nested container
1040     */
1041    protected boolean isNested() {
1042
1043        return (m_parentContainer != null) && (m_parentElement != null);
1044    }
1045
1046    /**
1047     * Prints the closing tag for an element wrapper if in online mode.<p>
1048     *
1049     * @param isGroupcontainer <code>true</code> if element is a group-container
1050     *
1051     * @throws IOException if the output fails
1052     */
1053    protected void printElementWrapperTagEnd(boolean isGroupcontainer) throws IOException {
1054
1055        if (m_editableRequest) {
1056            String result;
1057            if (isGroupcontainer) {
1058                result = "</div>";
1059            } else {
1060                result = "<div class=\""
1061                    + CmsContainerElement.CLASS_CONTAINER_ELEMENT_END_MARKER
1062                    + "\" style=\"display:none\"></div>";
1063            }
1064            pageContext.getOut().print(result);
1065        }
1066    }
1067
1068    /**
1069     * Prints the opening element wrapper tag for the container page editor if we are in Offline mode.<p>
1070     *
1071     * @param cms the Cms context
1072     * @param elementBean the element bean
1073     * @param page the container page
1074     * @param isGroupContainer true if the element is a group-container
1075     *
1076     * @throws Exception if something goes wrong
1077     */
1078    protected void printElementWrapperTagStart(
1079        CmsObject cms,
1080        CmsContainerElementBean elementBean,
1081        CmsContainerPageBean page,
1082        boolean isGroupContainer)
1083    throws Exception {
1084
1085        if (m_editableRequest) {
1086            StringBuffer result = new StringBuffer("<div class='");
1087            if (isGroupContainer) {
1088                result.append(CmsContainerElement.CLASS_GROUP_CONTAINER_ELEMENT_MARKER);
1089            } else {
1090                result.append(CmsContainerElement.CLASS_CONTAINER_ELEMENT_START_MARKER);
1091            }
1092            String serializedElement = getElementInfo(cms, elementBean, page);
1093            result.append("'");
1094            result.append(" " + CmsGwtConstants.ATTR_DATA_ELEMENT + "='").append(serializedElement);
1095            if (isGroupContainer) {
1096                result.append("'>");
1097            } else {
1098                result.append("' style='display:none;'></div>");
1099            }
1100            pageContext.getOut().print(result);
1101        }
1102    }
1103
1104    /**
1105     * Generates the detail view element.<p>
1106     *
1107     * @param request the current request
1108     * @param cms the CMS context
1109     * @param detailContent the detail content resource
1110     * @param container the container
1111     *
1112     * @return the detail view element
1113     */
1114    private CmsContainerElementBean generateDetailViewElement(
1115        ServletRequest request,
1116        CmsObject cms,
1117        CmsResource detailContent,
1118        CmsContainerBean container) {
1119
1120        CmsContainerElementBean element = null;
1121        if (detailContent != null) {
1122            // get the right formatter
1123
1124            CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache(
1125                cms,
1126                cms.getRequestContext().getRootUri());
1127            CmsFormatterConfiguration formatters = config.getFormatters(cms, detailContent);
1128            I_CmsFormatterBean formatter = formatters.getDetailFormatter(getType(), getContainerWidth());
1129
1130            if (formatter != null) {
1131                // use structure id as the instance id to enable use of nested containers
1132                Map<String, String> settings = new HashMap<String, String>();
1133                for (CmsContainerElementBean el : container.getElements()) {
1134                    try {
1135                        el.initResource(cms);
1136                        if (el.getResource().getTypeId() == detailContent.getTypeId()) {
1137                            settings.putAll(el.getIndividualSettings());
1138                            break;
1139                        }
1140                    } catch (CmsException e) {
1141                        LOG.error(e.getLocalizedMessage(), e);
1142                    }
1143                }
1144
1145                String formatterKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName());
1146                if (settings.containsKey(formatterKey)) {
1147                    String formatterConfigId = settings.get(formatterKey);
1148                    I_CmsFormatterBean dynamicFmt = config.findFormatter(formatterConfigId);
1149                    if (dynamicFmt != null) {
1150                        formatter = dynamicFmt;
1151                    }
1152                }
1153                settings.put(formatterKey, formatter.getKeyOrId());
1154                settings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString());
1155                // create element bean
1156                element = new CmsContainerElementBean(
1157                    detailContent.getStructureId(),
1158                    formatter.getJspStructureId(),
1159                    settings,
1160                    false);
1161                String pageRootPath = cms.getRequestContext().addSiteRoot(cms.getRequestContext().getUri());
1162                element = CmsTemplateMapper.get(request).transformDetailElement(cms, element, pageRootPath);
1163            }
1164        }
1165        return element;
1166    }
1167
1168    /**
1169     * Gets the container width as a number.<p>
1170     *
1171     * If the container width is not set, or not a number, -1 will be returned.<p>
1172     *
1173     * @return the container width or -1
1174     */
1175    private int getContainerWidth() {
1176
1177        int containerWidth = -1;
1178        try {
1179            containerWidth = Integer.parseInt(m_width);
1180        } catch (NumberFormatException e) {
1181            // do nothing, set width to -1
1182            LOG.debug("Error parsing container width.", e);
1183        }
1184        return containerWidth;
1185    }
1186
1187    /**
1188     * Returns the detail function element.<p>
1189     *
1190     * @param cms the cms context
1191     * @param detailFunctionPage the detail function page
1192     * @param req the current request
1193     *
1194     * @return the detail function element, if available
1195     */
1196    private CmsContainerElementBean getDetailFunctionElement(
1197        CmsObject cms,
1198        CmsResource detailFunctionPage,
1199        ServletRequest req) {
1200
1201        try {
1202            CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(cms, detailFunctionPage, req);
1203
1204            CmsContainerPageBean page = xmlContainerPage.getContainerPage(cms);
1205            CmsContainerBean container = page.getContainers().get(DETAIL_FUNCTION_CONTAINER_NAME);
1206            if (container == null) {
1207                for (Entry<String, CmsContainerBean> entry : page.getContainers().entrySet()) {
1208                    if (entry.getKey().endsWith("-" + DETAIL_FUNCTION_CONTAINER_NAME)) {
1209                        container = entry.getValue();
1210                        break;
1211                    }
1212                }
1213            }
1214            if (container != null) {
1215                return container.getElements().get(0);
1216            }
1217        } catch (CmsException e) {
1218            LOG.error(e.getLocalizedMessage(), e);
1219        }
1220        return null;
1221    }
1222
1223    /**
1224     * Returns the serialized element data.<p>
1225     *
1226     * @param cms the current cms context
1227     * @param elementBean the element to serialize
1228     * @param page the container page
1229     *
1230     * @return the serialized element data
1231     *
1232     * @throws Exception if something goes wrong
1233     */
1234    private String getElementInfo(CmsObject cms, CmsContainerElementBean elementBean, CmsContainerPageBean page)
1235    throws Exception {
1236
1237        return CmsContainerpageService.getSerializedElementInfo(
1238            cms,
1239            (HttpServletRequest)pageContext.getRequest(),
1240            (HttpServletResponse)pageContext.getResponse(),
1241            elementBean,
1242            page);
1243    }
1244
1245    /**
1246     * Parses the maximum element number from the current container and returns the resulting number.<p>
1247     *
1248     * @param requestUri the requested URI
1249     *
1250     * @return the maximum number of elements of the container
1251     */
1252    private int getMaxElements(String requestUri) {
1253
1254        String containerMaxElements = getMaxElements();
1255
1256        int maxElements = -1;
1257        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(containerMaxElements)) {
1258            try {
1259                maxElements = Integer.parseInt(containerMaxElements);
1260            } catch (NumberFormatException e) {
1261                throw new CmsIllegalStateException(
1262                    Messages.get().container(
1263                        Messages.LOG_WRONG_CONTAINER_MAXELEMENTS_3,
1264                        new Object[] {requestUri, getName(), containerMaxElements}),
1265                    e);
1266            }
1267        } else {
1268            if (LOG.isWarnEnabled()) {
1269                LOG.warn(
1270                    Messages.get().getBundle().key(
1271                        Messages.LOG_MAXELEMENTS_NOT_SET_2,
1272                        new Object[] {getName(), requestUri}));
1273            }
1274        }
1275        return maxElements;
1276    }
1277
1278    /**
1279     * Returns the ADE session cache for container elements in case of an editable request, otherwise <code>null</code>.<p>
1280     *
1281     * @param cms the cms context
1282     *
1283     * @return the session cache
1284     */
1285    private CmsADESessionCache getSessionCache(CmsObject cms) {
1286
1287        return m_editableRequest
1288        ? CmsADESessionCache.getCache((HttpServletRequest)(pageContext.getRequest()), cms)
1289        : null;
1290    }
1291
1292    /**
1293     * Evaluates if this container is nested within a model group.<p>
1294     *
1295     * @param standardContext the standard context
1296     *
1297     * @return <code>true</code> if the container has model group ancestors
1298     */
1299    private boolean hasModelGroupAncestor(CmsJspStandardContextBean standardContext) {
1300
1301        boolean result = false;
1302        if (!standardContext.isModelGroupPage()) {
1303            CmsContainerElementWrapper parent = standardContext.getElement();
1304            while ((parent != null) && !result) {
1305                result = parent.isModelGroup();
1306                parent = parent.getParent();
1307            }
1308        }
1309        return result;
1310    }
1311
1312    /**
1313     * Prints an element error tag to the response out.<p>
1314     *
1315     * @param elementSitePath the element site path
1316     * @param formatterSitePath the formatter site path
1317     * @param exception the exception causing the error
1318     *
1319     * @throws IOException if something goes wrong writing to response out
1320     */
1321    private void printElementErrorTag(String elementSitePath, String formatterSitePath, Exception exception)
1322    throws IOException {
1323
1324        if (m_editableRequest) {
1325            String stacktrace = CmsException.getStackTraceAsString(exception);
1326            if (CmsStringUtil.isEmptyOrWhitespaceOnly(stacktrace)) {
1327                stacktrace = null;
1328            } else {
1329                // stacktrace = CmsStringUtil.escapeJavaScript(stacktrace);
1330                stacktrace = CmsEncoder.escapeXml(stacktrace);
1331            }
1332            StringBuffer errorBox = new StringBuffer(256);
1333            errorBox.append(
1334                "<div style=\"display:block; padding: 5px; border: red solid 2px; color: black; background: white;\" class=\"");
1335            errorBox.append(CmsContainerElement.CLASS_ELEMENT_ERROR);
1336            errorBox.append("\">");
1337            errorBox.append(
1338                Messages.get().getBundle().key(
1339                    Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2,
1340                    elementSitePath,
1341                    formatterSitePath));
1342            errorBox.append("<br />");
1343            errorBox.append(exception.getLocalizedMessage());
1344            if (stacktrace != null) {
1345                errorBox.append(
1346                    "<span onclick=\"opencms.openStacktraceDialog(event);\" style=\"border: 1px solid black; cursor: pointer;\">");
1347                errorBox.append(Messages.get().getBundle().key(Messages.GUI_LABEL_STACKTRACE_0));
1348                String title = Messages.get().getBundle().key(
1349                    Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2,
1350                    elementSitePath,
1351                    formatterSitePath);
1352                errorBox.append("<span title=\"");
1353                errorBox.append(CmsEncoder.escapeXml(title));
1354                errorBox.append("\" class=\"hiddenStacktrace\" style=\"display:none;\">");
1355                errorBox.append(stacktrace);
1356                errorBox.append("</span></span>");
1357            }
1358            errorBox.append("</div>");
1359            pageContext.getOut().print(errorBox.toString());
1360        }
1361    }
1362
1363    /**
1364     * Renders a container element.<p>
1365     *
1366     * @param request the current request
1367     * @param cms the CMS context
1368     * @param standardContext the current standard contxt bean
1369     * @param element the container element to render
1370     * @param locale the requested locale
1371     * @param alreadyFull if true, only render invisible elements (they don't count towards the "max elements")
1372     *
1373     * @return true if an element was rendered that counts towards the container's maximum number of elements
1374     *
1375     * @throws Exception if something goes wrong
1376     */
1377    private boolean renderContainerElement(
1378        HttpServletRequest request,
1379        CmsObject cms,
1380        CmsJspStandardContextBean standardContext,
1381        CmsContainerElementBean element,
1382        Locale locale,
1383        boolean alreadyFull)
1384    throws Exception {
1385
1386        CmsTemplateContext context = (CmsTemplateContext)(request.getAttribute(
1387            CmsTemplateContextManager.ATTR_TEMPLATE_CONTEXT));
1388        if ((context == null) && alreadyFull) {
1389            return false;
1390        }
1391        String contextKey = null;
1392        if (context != null) {
1393            contextKey = context.getKey();
1394        } else {
1395            String rpcContextOverride = (String)request.getAttribute(
1396                CmsTemplateContextManager.ATTR_RPC_CONTEXT_OVERRIDE);
1397            contextKey = rpcContextOverride;
1398        }
1399        boolean ignoreTemplateContexts = false;
1400        try {
1401            I_CmsTemplateContextProvider templateProvider = null;
1402            if (context != null) {
1403                templateProvider = context.getProvider();
1404            }
1405            if (templateProvider == null) {
1406                templateProvider = OpenCms.getTemplateContextManager().getTemplateContextProvider(cms, cms.getRequestContext().getUri());
1407            }
1408            ignoreTemplateContexts = (templateProvider != null) && templateProvider.isIgnoreTemplateContextsSetting();
1409        } catch (CmsException e) {
1410            LOG.info(e.getLocalizedMessage(), e);
1411        }
1412        boolean showInContext = ignoreTemplateContexts || shouldShowInContext(element, context != null ? context.getKey() : null);
1413        boolean isOnline = cms.getRequestContext().getCurrentProject().isOnlineProject();
1414        if (!m_editableRequest && !showInContext) {
1415            return false;
1416        }
1417        try {
1418            element.initResource(cms);
1419        } catch (CmsPermissionViolationException e) {
1420            LOG.info(e.getLocalizedMessage(), e);
1421            return false;
1422        }
1423        if (!m_editableRequest && !element.isReleasedAndNotExpired()) {
1424            // do not render expired resources for the online project
1425            return false;
1426        }
1427        ServletRequest req = pageContext.getRequest();
1428        ServletResponse res = pageContext.getResponse();
1429        String containerType = getType();
1430        int containerWidth = getContainerWidth();
1431        CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfigurationWithCache(
1432            cms,
1433            cms.getRequestContext().getRootUri());
1434        boolean isGroupContainer = element.isGroupContainer(cms);
1435        boolean isInheritedContainer = element.isInheritedContainer(cms);
1436        I_CmsFormatterBean formatterConfig = null;
1437        if (!isGroupContainer && !isInheritedContainer) {
1438            // ensure that the formatter configuration id is added to the element settings, so it will be persisted on save
1439            formatterConfig = ensureValidFormatterSettings(
1440                cms,
1441                element,
1442                adeConfig,
1443                getName(),
1444                containerType,
1445                containerWidth);
1446            element.initSettings(cms, adeConfig, formatterConfig, locale, request, m_settingPresets);
1447        }
1448        // writing elements to the session cache to improve performance of the container-page editor in offline project
1449        if (m_editableRequest) {
1450            getSessionCache(cms).setCacheContainerElement(element.editorHash(), element);
1451        }
1452
1453        if (isGroupContainer || isInheritedContainer) {
1454            if (alreadyFull) {
1455                return false;
1456            }
1457            List<CmsContainerElementBean> subElements;
1458            if (isGroupContainer) {
1459                subElements = getGroupContainerElements(cms, element, req, containerType);
1460            } else {
1461                // inherited container case
1462                subElements = getInheritedContainerElements(cms, element);
1463            }
1464            // wrapping the elements with DIV containing initial element data. To be removed by the container-page editor
1465            printElementWrapperTagStart(cms, element, standardContext.getPage(), true);
1466            for (CmsContainerElementBean subelement : subElements) {
1467
1468                try {
1469                    subelement.initResource(cms);
1470                    boolean shouldShowSubElementInContext = ignoreTemplateContexts || shouldShowInContext(subelement, contextKey);
1471                    if (!m_editableRequest
1472                        && (!shouldShowSubElementInContext || !subelement.isReleasedAndNotExpired())) {
1473                        continue;
1474                    }
1475                    I_CmsFormatterBean subElementFormatterConfig = ensureValidFormatterSettings(
1476                        cms,
1477                        subelement,
1478                        adeConfig,
1479                        getName(),
1480                        containerType,
1481                        containerWidth);
1482                    subelement.initSettings(
1483                        cms,
1484                        adeConfig,
1485                        subElementFormatterConfig,
1486                        locale,
1487                        request,
1488                        m_settingPresets);
1489                    // writing elements to the session cache to improve performance of the container-page editor
1490                    if (m_editableRequest) {
1491                        getSessionCache(cms).setCacheContainerElement(subelement.editorHash(), subelement);
1492                    }
1493                    if (subElementFormatterConfig == null) {
1494                        if (LOG.isErrorEnabled()) {
1495                            LOG.error(
1496                                new CmsIllegalStateException(
1497                                    Messages.get().container(
1498                                        Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3,
1499                                        subelement.getSitePath(),
1500                                        OpenCms.getResourceManager().getResourceType(
1501                                            subelement.getResource()).getTypeName(),
1502                                        containerType)));
1503                        }
1504                        // skip this element, it has no formatter for this container type defined
1505                        continue;
1506                    }
1507                    // execute the formatter JSP for the given element URI
1508                    // wrapping the elements with DIV containing initial element data. To be removed by the container-page editor
1509                    printElementWrapperTagStart(cms, subelement, standardContext.getPage(), false);
1510                    standardContext.setElement(subelement);
1511                    try {
1512                        String formatterSitePath;
1513                        try {
1514                            CmsResource formatterResource = cms.readResource(
1515                                subElementFormatterConfig.getJspStructureId());
1516                            formatterSitePath = cms.getSitePath(formatterResource);
1517                        } catch (CmsVfsResourceNotFoundException ex) {
1518                            LOG.debug("Formatter JSP not found by id, try using path.", ex);
1519                            formatterSitePath = cms.getRequestContext().removeSiteRoot(
1520                                subElementFormatterConfig.getJspRootPath());
1521                        }
1522                        if (shouldShowSubElementInContext) {
1523                            CmsJspTagInclude.includeTagAction(
1524                                pageContext,
1525                                formatterSitePath,
1526                                null,
1527                                locale,
1528                                false,
1529                                isOnline && m_cacheable,
1530                                null,
1531                                CmsRequestUtil.getAttributeMap(req),
1532                                req,
1533                                res);
1534                        } else {
1535                            pageContext.getOut().print(DUMMY_ELEMENT);
1536                        }
1537                    } catch (Exception e) {
1538                        if (LOG.isErrorEnabled()) {
1539                            LOG.error(
1540                                Messages.get().getBundle().key(
1541                                    Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2,
1542                                    subelement.getSitePath(),
1543                                    subElementFormatterConfig),
1544                                e);
1545                        }
1546                        printElementErrorTag(subelement.getSitePath(), subElementFormatterConfig.getJspRootPath(), e);
1547                    }
1548                    printElementWrapperTagEnd(false);
1549                } catch (Exception e) {
1550                    if (LOG.isErrorEnabled()) {
1551                        LOG.error(e.getLocalizedMessage(), e);
1552                    }
1553                }
1554            }
1555            printElementWrapperTagEnd(true);
1556            return true;
1557        } else {
1558            boolean result = true;
1559            if (alreadyFull) {
1560                result = false;
1561                if (!showInContext) {
1562                    printElementWrapperTagStart(cms, element, standardContext.getPage(), false);
1563                    pageContext.getOut().print(DUMMY_ELEMENT);
1564                    printElementWrapperTagEnd(false);
1565                }
1566            } else {
1567                String formatter = null;
1568                try {
1569                    if (formatterConfig != null) {
1570                        try {
1571                            CmsResource formatterResource = cms.readResource(formatterConfig.getJspStructureId());
1572                            formatter = cms.getSitePath(formatterResource);
1573                        } catch (CmsVfsResourceNotFoundException ex) {
1574                            LOG.debug("Formatter JSP not found by id, try using path.", ex);
1575                            if (cms.existsResource(
1576                                cms.getRequestContext().removeSiteRoot(formatterConfig.getJspRootPath()))) {
1577                                formatter = cms.getRequestContext().removeSiteRoot(formatterConfig.getJspRootPath());
1578                            }
1579                        }
1580                    } else {
1581                        formatter = cms.getSitePath(cms.readResource(element.getFormatterId()));
1582                    }
1583                } catch (CmsException e) {
1584                    LOG.debug("Formatter resource can not be found, try reading it from the configuration.", e);
1585                    // the formatter resource can not be found, try reading it form the configuration
1586                    CmsFormatterConfiguration elementFormatters = adeConfig.getFormatters(cms, element.getResource());
1587                    I_CmsFormatterBean elementFormatterBean = elementFormatters.getDefaultFormatter(
1588                        containerType,
1589                        containerWidth);
1590                    if (elementFormatterBean == null) {
1591                        if (LOG.isErrorEnabled()) {
1592                            LOG.error(
1593                                new CmsIllegalStateException(
1594                                    Messages.get().container(
1595                                        Messages.ERR_XSD_NO_TEMPLATE_FORMATTER_3,
1596                                        element.getSitePath(),
1597                                        OpenCms.getResourceManager().getResourceType(
1598                                            element.getResource()).getTypeName(),
1599                                        containerType)));
1600                        }
1601                        // skip this element, it has no formatter for this container type defined
1602                        return false;
1603                    }
1604                    try {
1605                        CmsResource formatterResource = cms.readResource(elementFormatterBean.getJspStructureId());
1606                        formatter = cms.getSitePath(formatterResource);
1607                    } catch (CmsVfsResourceNotFoundException ex) {
1608                        LOG.debug("Formatter JSP not found by id, try using path.", ex);
1609                        formatter = cms.getRequestContext().removeSiteRoot(elementFormatterBean.getJspRootPath());
1610                    }
1611                }
1612
1613                printElementWrapperTagStart(cms, element, standardContext.getPage(), false);
1614                standardContext.setElement(element);
1615                try {
1616                    if (!showInContext) {
1617                        // write invisible dummy element
1618                        pageContext.getOut().print(DUMMY_ELEMENT);
1619                        result = false;
1620                    } else {
1621                        // execute the formatter jsp for the given element uri
1622                        CmsJspTagInclude.includeTagAction(
1623                            pageContext,
1624                            formatter,
1625                            null,
1626                            locale,
1627                            false,
1628                            isOnline && m_cacheable,
1629                            null,
1630                            CmsRequestUtil.getAtrributeMap(req),
1631                            req,
1632                            res);
1633                    }
1634                } catch (Exception e) {
1635                    if (LOG.isErrorEnabled()) {
1636                        LOG.error(
1637                            Messages.get().getBundle().key(
1638                                Messages.ERR_CONTAINER_PAGE_ELEMENT_RENDER_ERROR_2,
1639                                element.getSitePath(),
1640                                formatter),
1641                            e);
1642                    }
1643                    printElementErrorTag(element.getSitePath(), formatter, e);
1644                }
1645                printElementWrapperTagEnd(false);
1646            }
1647            return result;
1648        }
1649    }
1650
1651    /**
1652     * Resets the tag instance and standard context state.<p>
1653     */
1654    private void resetState() {
1655
1656        // clear all members so the tag object may be reused
1657        m_type = null;
1658        m_name = null;
1659        m_param = null;
1660        m_maxElements = null;
1661        m_tag = null;
1662        m_tagClass = null;
1663        m_detailView = false;
1664        m_detailOnly = false;
1665        m_width = null;
1666        m_editableBy = null;
1667        m_bodyContent = null;
1668        m_hasModelGroupAncestor = false;
1669        // reset the current element
1670        CmsJspStandardContextBean cmsContext = CmsJspStandardContextBean.getInstance(pageContext.getRequest());
1671        cmsContext.setElement(m_parentElement);
1672        cmsContext.setContainer(m_parentContainer);
1673        m_parentElement = null;
1674        m_parentContainer = null;
1675    }
1676
1677    /**
1678     * Helper method to determine whether an element should be shown in a context.<p>
1679     *
1680     * @param element the element for which the visibility should be determined
1681     * @param contextKey the key of the context for which to check
1682     *
1683     * @return true if the current context doesn't prohibit the element from being shown
1684     */
1685    private boolean shouldShowInContext(CmsContainerElementBean element, String contextKey) {
1686
1687        if (contextKey == null) {
1688            return true;
1689        }
1690
1691        try {
1692            if ((element.getResource() != null)
1693                && !OpenCms.getTemplateContextManager().shouldShowType(
1694                    contextKey,
1695                    OpenCms.getResourceManager().getResourceType(element.getResource().getTypeId()).getTypeName())) {
1696                return false;
1697            }
1698        } catch (CmsLoaderException e) {
1699            // ignore and log
1700            LOG.error(e.getLocalizedMessage(), e);
1701        }
1702        Map<String, String> settings = element.getSettings();
1703        if (settings == null) {
1704            return true;
1705        }
1706        String contextsAllowed = settings.get(CmsTemplateContextInfo.SETTING);
1707        if (contextsAllowed == null) {
1708            return true;
1709        }
1710        if (contextsAllowed.equals(CmsTemplateContextInfo.EMPTY_VALUE)) {
1711            return false;
1712        }
1713
1714        List<String> contextsAllowedList = CmsStringUtil.splitAsList(contextsAllowed, "|");
1715        if (!contextsAllowedList.contains(contextKey)) {
1716            return false;
1717        }
1718        return true;
1719    }
1720}