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, 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.util;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsADEManager;
032import org.opencms.ade.configuration.CmsFunctionReference;
033import org.opencms.ade.containerpage.CmsContainerpageService;
034import org.opencms.ade.containerpage.CmsModelGroupHelper;
035import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
036import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
037import org.opencms.ade.detailpage.CmsDetailPageInfo;
038import org.opencms.ade.detailpage.CmsDetailPageResourceHandler;
039import org.opencms.file.CmsFile;
040import org.opencms.file.CmsObject;
041import org.opencms.file.CmsPropertyDefinition;
042import org.opencms.file.CmsRequestContext;
043import org.opencms.file.CmsResource;
044import org.opencms.file.CmsResourceFilter;
045import org.opencms.file.history.CmsHistoryResourceHandler;
046import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
047import org.opencms.flex.CmsFlexController;
048import org.opencms.flex.CmsFlexRequest;
049import org.opencms.gwt.shared.CmsGwtConstants;
050import org.opencms.i18n.CmsLocaleGroupService;
051import org.opencms.jsp.CmsJspBean;
052import org.opencms.jsp.CmsJspResourceWrapper;
053import org.opencms.jsp.CmsJspTagContainer;
054import org.opencms.jsp.CmsJspTagEditable;
055import org.opencms.jsp.Messages;
056import org.opencms.jsp.jsonpart.CmsJsonPartFilter;
057import org.opencms.loader.CmsTemplateContextManager;
058import org.opencms.main.CmsException;
059import org.opencms.main.CmsLog;
060import org.opencms.main.CmsRuntimeException;
061import org.opencms.main.CmsSystemInfo;
062import org.opencms.main.OpenCms;
063import org.opencms.relations.CmsCategory;
064import org.opencms.relations.CmsCategoryService;
065import org.opencms.search.galleries.CmsGalleryNameMacroResolver;
066import org.opencms.site.CmsSite;
067import org.opencms.util.CmsCollectionsGenericWrapper;
068import org.opencms.util.CmsMacroResolver;
069import org.opencms.util.CmsStringUtil;
070import org.opencms.util.CmsUUID;
071import org.opencms.xml.containerpage.CmsADESessionCache;
072import org.opencms.xml.containerpage.CmsContainerBean;
073import org.opencms.xml.containerpage.CmsContainerElementBean;
074import org.opencms.xml.containerpage.CmsContainerPageBean;
075import org.opencms.xml.containerpage.CmsDynamicFunctionBean;
076import org.opencms.xml.containerpage.CmsDynamicFunctionParser;
077import org.opencms.xml.containerpage.CmsFormatterConfiguration;
078import org.opencms.xml.containerpage.CmsMetaMapping;
079import org.opencms.xml.containerpage.CmsXmlContainerPage;
080import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
081import org.opencms.xml.containerpage.I_CmsFormatterBean;
082import org.opencms.xml.content.CmsXmlContent;
083import org.opencms.xml.content.CmsXmlContentFactory;
084import org.opencms.xml.content.CmsXmlContentProperty;
085import org.opencms.xml.templatemapper.CmsTemplateMapper;
086import org.opencms.xml.types.I_CmsXmlContentValue;
087
088import java.lang.reflect.Constructor;
089import java.lang.reflect.Method;
090import java.util.ArrayList;
091import java.util.Arrays;
092import java.util.Collections;
093import java.util.HashMap;
094import java.util.List;
095import java.util.Locale;
096import java.util.Map;
097
098import javax.servlet.ServletRequest;
099import javax.servlet.http.HttpServletRequest;
100
101import org.apache.commons.collections.Transformer;
102import org.apache.commons.lang3.LocaleUtils;
103import org.apache.commons.logging.Log;
104
105/**
106 * Allows convenient access to the most important OpenCms functions on a JSP page,
107 * indented to be used from a JSP with the JSTL or EL.<p>
108 *
109 * This bean is available by default in the context of an OpenCms managed JSP.<p>
110 *
111 * @since 8.0
112 */
113public final class CmsJspStandardContextBean {
114
115    /**
116     * Container element wrapper to add some API methods.<p>
117     */
118    public class CmsContainerElementWrapper extends CmsContainerElementBean {
119
120        /** The wrapped element instance. */
121        private CmsContainerElementBean m_wrappedElement;
122
123        /**
124         * Constructor.<p>
125         *
126         * @param element the element to wrap
127         */
128        protected CmsContainerElementWrapper(CmsContainerElementBean element) {
129
130            m_wrappedElement = element;
131
132        }
133
134        /**
135         * @see org.opencms.xml.containerpage.CmsContainerElementBean#clone()
136         */
137        @Override
138        public CmsContainerElementBean clone() {
139
140            return m_wrappedElement.clone();
141        }
142
143        /**
144         * @see org.opencms.xml.containerpage.CmsContainerElementBean#editorHash()
145         */
146        @Override
147        public String editorHash() {
148
149            return m_wrappedElement.editorHash();
150        }
151
152        /**
153         * @see org.opencms.xml.containerpage.CmsContainerElementBean#equals(java.lang.Object)
154         */
155        @Override
156        public boolean equals(Object obj) {
157
158            return m_wrappedElement.equals(obj);
159        }
160
161        /**
162         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getFormatterId()
163         */
164        @Override
165        public CmsUUID getFormatterId() {
166
167            return m_wrappedElement.getFormatterId();
168        }
169
170        /**
171         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getId()
172         */
173        @Override
174        public CmsUUID getId() {
175
176            return m_wrappedElement.getId();
177        }
178
179        /**
180         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getIndividualSettings()
181         */
182        @Override
183        public Map<String, String> getIndividualSettings() {
184
185            return m_wrappedElement.getIndividualSettings();
186        }
187
188        /**
189         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInheritanceInfo()
190         */
191        @Override
192        public CmsInheritanceInfo getInheritanceInfo() {
193
194            return m_wrappedElement.getInheritanceInfo();
195        }
196
197        /**
198         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInstanceId()
199         */
200        @Override
201        public String getInstanceId() {
202
203            return m_wrappedElement.getInstanceId();
204        }
205
206        /**
207         * Returns the parent element if present.<p>
208         *
209         * @return the parent element or <code>null</code> if not available
210         */
211        public CmsContainerElementWrapper getParent() {
212
213            CmsContainerElementBean parent = getParentElement(m_wrappedElement);
214            return parent != null ? new CmsContainerElementWrapper(getParentElement(m_wrappedElement)) : null;
215        }
216
217        /**
218         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getResource()
219         */
220        @Override
221        public CmsResource getResource() {
222
223            return m_wrappedElement.getResource();
224        }
225
226        /**
227         * Returns the resource type name of the element resource.<p>
228         *
229         * @return the resource type name
230         */
231        public String getResourceTypeName() {
232
233            String result = "";
234            try {
235                result = OpenCms.getResourceManager().getResourceType(m_wrappedElement.getResource()).getTypeName();
236            } catch (Exception e) {
237                CmsJspStandardContextBean.LOG.error(e.getLocalizedMessage(), e);
238            }
239            return result;
240        }
241
242        /**
243         * Returns a lazy initialized setting map.<p>
244         *
245         * The values returned in the map are instances of {@link A_CmsJspValueWrapper}.
246         *
247         * @return the wrapped settings
248         */
249        public Map<String, CmsJspElementSettingValueWrapper> getSetting() {
250
251            return CmsCollectionsGenericWrapper.createLazyMap(new SettingsTransformer(m_wrappedElement));
252        }
253
254        /**
255         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSettings()
256         */
257        @Override
258        public Map<String, String> getSettings() {
259
260            return m_wrappedElement.getSettings();
261        }
262
263        /**
264         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSitePath()
265         */
266        @Override
267        public String getSitePath() {
268
269            return m_wrappedElement.getSitePath();
270        }
271
272        /**
273         * @see org.opencms.xml.containerpage.CmsContainerElementBean#hashCode()
274         */
275        @Override
276        public int hashCode() {
277
278            return m_wrappedElement.hashCode();
279        }
280
281        /**
282         * @see org.opencms.xml.containerpage.CmsContainerElementBean#initResource(org.opencms.file.CmsObject)
283         */
284        @Override
285        public void initResource(CmsObject cms) throws CmsException {
286
287            m_wrappedElement.initResource(cms);
288        }
289
290        /**
291         * @see org.opencms.xml.containerpage.CmsContainerElementBean#initSettings(org.opencms.file.CmsObject, org.opencms.xml.containerpage.I_CmsFormatterBean, java.util.Locale, javax.servlet.ServletRequest, java.util.Map)
292         */
293        @Override
294        public void initSettings(
295            CmsObject cms,
296            I_CmsFormatterBean formatterBean,
297            Locale locale,
298            ServletRequest request,
299            Map<String, String> settingPresets) {
300
301            m_wrappedElement.initSettings(cms, formatterBean, locale, request, settingPresets);
302        }
303
304        /**
305         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isCreateNew()
306         */
307        @Override
308        public boolean isCreateNew() {
309
310            return m_wrappedElement.isCreateNew();
311        }
312
313        /**
314         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isGroupContainer(org.opencms.file.CmsObject)
315         */
316        @Override
317        public boolean isGroupContainer(CmsObject cms) throws CmsException {
318
319            return m_wrappedElement.isGroupContainer(cms);
320        }
321
322        /**
323         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInheritedContainer(org.opencms.file.CmsObject)
324         */
325        @Override
326        public boolean isInheritedContainer(CmsObject cms) throws CmsException {
327
328            return m_wrappedElement.isInheritedContainer(cms);
329        }
330
331        /**
332         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInMemoryOnly()
333         */
334        @Override
335        public boolean isInMemoryOnly() {
336
337            return m_wrappedElement.isInMemoryOnly();
338        }
339
340        /**
341         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isReleasedAndNotExpired()
342         */
343        @Override
344        public boolean isReleasedAndNotExpired() {
345
346            return m_wrappedElement.isReleasedAndNotExpired();
347        }
348
349        /**
350         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isTemporaryContent()
351         */
352        @Override
353        public boolean isTemporaryContent() {
354
355            return m_wrappedElement.isTemporaryContent();
356        }
357
358        /**
359         * @see org.opencms.xml.containerpage.CmsContainerElementBean#removeInstanceId()
360         */
361        @Override
362        public void removeInstanceId() {
363
364            m_wrappedElement.removeInstanceId();
365        }
366
367        /**
368         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setFormatterId(org.opencms.util.CmsUUID)
369         */
370        @Override
371        public void setFormatterId(CmsUUID formatterId) {
372
373            m_wrappedElement.setFormatterId(formatterId);
374        }
375
376        /**
377         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setHistoryFile(org.opencms.file.CmsFile)
378         */
379        @Override
380        public void setHistoryFile(CmsFile file) {
381
382            m_wrappedElement.setHistoryFile(file);
383        }
384
385        /**
386         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setInheritanceInfo(org.opencms.ade.containerpage.shared.CmsInheritanceInfo)
387         */
388        @Override
389        public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) {
390
391            m_wrappedElement.setInheritanceInfo(inheritanceInfo);
392        }
393
394        /**
395         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setTemporaryFile(org.opencms.file.CmsFile)
396         */
397        @Override
398        public void setTemporaryFile(CmsFile elementFile) {
399
400            m_wrappedElement.setTemporaryFile(elementFile);
401        }
402
403        /**
404         * @see org.opencms.xml.containerpage.CmsContainerElementBean#toString()
405         */
406        @Override
407        public String toString() {
408
409            return m_wrappedElement.toString();
410        }
411    }
412
413    /**
414     * Provides a lazy initialized Map that provides the detail page link as a value when given the name of a
415     * (named) dynamic function or resource type as a key.<p>
416     */
417    public class CmsDetailLookupTransformer implements Transformer {
418
419        /** The selected prefix. */
420        private String m_prefix;
421
422        /**
423         * Constructor with a prefix.<p>
424         *
425         * The prefix is used to distinguish between type detail pages and function detail pages.<p>
426         *
427         * @param prefix the prefix to use
428         */
429        public CmsDetailLookupTransformer(String prefix) {
430
431            m_prefix = prefix;
432        }
433
434        /**
435         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
436         */
437        @Override
438        public Object transform(Object input) {
439
440            String type = m_prefix + String.valueOf(input);
441            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
442                m_cms,
443                m_cms.addSiteRoot(m_cms.getRequestContext().getUri()));
444            List<CmsDetailPageInfo> detailPages = config.getDetailPagesForType(type);
445            CmsDetailPageInfo detailPage = null;
446            boolean usingDefault = false;
447            if ((detailPages == null) || (detailPages.size() == 0)) {
448                detailPage = config.getDefaultDetailPage();
449                usingDefault = true;
450            } else {
451                detailPage = detailPages.get(0);
452            }
453            if (detailPage == null) {
454                return "[No detail page configured for type =" + type + "=]";
455            }
456
457            CmsUUID id = detailPage.getId();
458            try {
459                CmsResource r = m_cms.readResource(id);
460                String link = OpenCms.getLinkManager().substituteLink(m_cms, r);
461                if (usingDefault) {
462                    link = CmsStringUtil.joinPaths(link, (String)input);
463                }
464                return link;
465            } catch (CmsException e) {
466                LOG.warn(e.getLocalizedMessage(), e);
467                return "[Error reading detail page for type =" + type + "=]";
468            }
469        }
470    }
471
472    /**
473     * The element setting transformer.<p>
474     */
475    public class SettingsTransformer implements Transformer {
476
477        /** The element formatter config. */
478        private I_CmsFormatterBean m_formatter;
479
480        /** The configured formatter settings. */
481        private Map<String, CmsXmlContentProperty> m_formatterSettingsConfig;
482
483        /** The element. */
484        private CmsContainerElementBean m_transformElement;
485
486        /**
487         * Constructor.<p>
488         *
489         * @param element the element
490         */
491        SettingsTransformer(CmsContainerElementBean element) {
492
493            m_transformElement = element;
494            m_formatter = getElementFormatter(element);
495        }
496
497        /**
498         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
499         */
500        @Override
501        public Object transform(Object settingName) {
502
503            boolean exists;
504            if (m_formatter != null) {
505                if (m_formatterSettingsConfig == null) {
506                    m_formatterSettingsConfig = OpenCms.getADEManager().getFormatterSettings(
507                        m_cms,
508                        m_formatter,
509                        m_transformElement.getResource(),
510                        getLocale(),
511                        m_request);
512                }
513                exists = m_formatterSettingsConfig.get(settingName) != null;
514            } else {
515                exists = m_transformElement.getSettings().get(settingName) != null;
516            }
517            return new CmsJspElementSettingValueWrapper(
518                CmsJspStandardContextBean.this,
519                m_transformElement.getSettings().get(settingName),
520                exists);
521        }
522    }
523
524    /**
525     * Bean containing a template name and URI.<p>
526     */
527    public static class TemplateBean {
528
529        /** True if the template context was manually selected. */
530        private boolean m_forced;
531
532        /** The template name. */
533        private String m_name;
534
535        /** The template resource. */
536        private CmsResource m_resource;
537
538        /** The template uri, if no resource is set. */
539        private String m_uri;
540
541        /**
542         * Creates a new instance.<p>
543         *
544         * @param name the template name
545         * @param resource the template resource
546         */
547        public TemplateBean(String name, CmsResource resource) {
548
549            m_resource = resource;
550            m_name = name;
551        }
552
553        /**
554         * Creates a new instance with an URI instead of a resoure.<p>
555         *
556         * @param name the template name
557         * @param uri the template uri
558         */
559        public TemplateBean(String name, String uri) {
560
561            m_name = name;
562            m_uri = uri;
563        }
564
565        /**
566         * Gets the template name.<p>
567         *
568         * @return the template name
569         */
570        public String getName() {
571
572            return m_name;
573        }
574
575        /**
576         * Gets the template resource.<p>
577         *
578         * @return the template resource
579         */
580        public CmsResource getResource() {
581
582            return m_resource;
583        }
584
585        /**
586         * Gets the template uri.<p>
587         *
588         * @return the template URI.
589         */
590        public String getUri() {
591
592            if (m_resource != null) {
593                return m_resource.getRootPath();
594            } else {
595                return m_uri;
596            }
597        }
598
599        /**
600         * Returns true if the template context was manually selected.<p>
601         *
602         * @return true if the template context was manually selected
603         */
604        public boolean isForced() {
605
606            return m_forced;
607        }
608
609        /**
610         * Sets the 'forced' flag to a new value.<p>
611         *
612         * @param forced the new value
613         */
614        public void setForced(boolean forced) {
615
616            m_forced = forced;
617        }
618
619    }
620
621    /**
622     * The meta mappings transformer.<p>
623     */
624    class MetaLookupTranformer implements Transformer {
625
626        /**
627         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
628         */
629        public Object transform(Object arg0) {
630
631            String result = null;
632            if ((m_metaMappings != null) && m_metaMappings.containsKey(arg0)) {
633                MetaMapping mapping = m_metaMappings.get(arg0);
634                CmsGalleryNameMacroResolver resolver = null;
635                try {
636                    CmsResourceFilter filter = getIsEditMode()
637                    ? CmsResourceFilter.IGNORE_EXPIRATION
638                    : CmsResourceFilter.DEFAULT;
639                    CmsResource res = m_cms.readResource(mapping.m_contentId, filter);
640                    CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, res, m_request);
641                    resolver = new CmsGalleryNameMacroResolver(m_cms, content, getLocale());
642                    if (content.hasLocale(getLocale())) {
643                        I_CmsXmlContentValue val = content.getValue(mapping.m_elementXPath, getLocale());
644                        if (val != null) {
645                            result = val.getStringValue(m_cms);
646                        }
647                    }
648
649                } catch (CmsException e) {
650                    LOG.error(e.getLocalizedMessage(), e);
651                }
652                if (result == null) {
653                    result = mapping.m_defaultValue;
654                }
655                if ((resolver != null) && (result != null)) {
656                    result = resolver.resolveMacros(result);
657                }
658            }
659            return result;
660        }
661
662    }
663
664    /** The meta mapping data. */
665    class MetaMapping {
666
667        /** The mapping content structure id. */
668        CmsUUID m_contentId;
669
670        /** The default value. */
671        String m_defaultValue;
672
673        /** The mapping value xpath. */
674        String m_elementXPath;
675
676        /** The mapping key. */
677        String m_key;
678
679        /** The mapping order. */
680        int m_order;
681    }
682
683    /** The attribute name of the cms object.*/
684    public static final String ATTRIBUTE_CMS_OBJECT = "__cmsObject";
685
686    /** The attribute name of the standard JSP context bean. */
687    public static final String ATTRIBUTE_NAME = "cms";
688
689    /** The logger instance for this class. */
690    protected static final Log LOG = CmsLog.getLog(CmsJspStandardContextBean.class);
691
692    /** OpenCms user context. */
693    protected CmsObject m_cms;
694
695    /** The meta mapping configuration. */
696    Map<String, MetaMapping> m_metaMappings;
697
698    /** The current request. */
699    ServletRequest m_request;
700
701    /** Lazily initialized map from a category path to all sub-categories of that category. */
702    private Map<String, CmsJspCategoryAccessBean> m_allSubCategories;
703
704    /** Lazily initialized map from a category path to the path's category object. */
705    private Map<String, CmsCategory> m_categories;
706
707    /** The container the currently rendered element is part of. */
708    private CmsContainerBean m_container;
709
710    /** The current detail content resource if available. */
711    private CmsResource m_detailContentResource;
712
713    /** The detail function page. */
714    private CmsResource m_detailFunctionPage;
715
716    /** The detail only page references containers that are only displayed in detail view. */
717    private CmsContainerPageBean m_detailOnlyPage;
718
719    /** Flag to indicate if element was just edited. */
720    private boolean m_edited;
721
722    /** The currently rendered element. */
723    private CmsContainerElementBean m_element;
724
725    /** The elements of the current page. */
726    private Map<String, CmsContainerElementBean> m_elementInstances;
727
728    /** The lazy initialized map which allows access to the dynamic function beans. */
729    private Map<String, CmsDynamicFunctionBeanWrapper> m_function;
730
731    /** The lazy initialized map for the function detail pages. */
732    private Map<String, String> m_functionDetailPage;
733
734    /** Indicates if in drag mode. */
735    private boolean m_isDragMode;
736
737    /** Stores the edit mode info. */
738    private Boolean m_isEditMode;
739
740    /** Lazily initialized map from the locale to the localized title property. */
741    private Map<String, String> m_localeTitles;
742
743    /** The currently displayed container page. */
744    private CmsContainerPageBean m_page;
745
746    /** The current container page resource, lazy initialized. */
747    private CmsJspResourceWrapper m_pageResource;
748
749    /** The parent containers to the given element instance ids. */
750    private Map<String, CmsContainerBean> m_parentContainers;
751
752    /** Lazily initialized map from a category path to all categories on that path. */
753    private Map<String, List<CmsCategory>> m_pathCategories;
754
755    /** Lazily initialized map from the root path of a resource to all categories assigned to the resource. */
756    private Map<String, CmsJspCategoryAccessBean> m_resourceCategories;
757
758    /** Map from root paths to site relative paths. */
759    private Map<String, String> m_sitePaths;
760
761    /** The lazy initialized map for the detail pages. */
762    private Map<String, String> m_typeDetailPage;
763
764    /** The VFS content access bean. */
765    private CmsJspVfsAccessBean m_vfsBean;
766
767    /**
768     * Creates an empty instance.<p>
769     */
770    private CmsJspStandardContextBean() {
771
772        // NOOP
773    }
774
775    /**
776     * Creates a new standard JSP context bean.
777     *
778     * @param req the current servlet request
779     */
780    private CmsJspStandardContextBean(ServletRequest req) {
781
782        CmsFlexController controller = CmsFlexController.getController(req);
783        m_request = req;
784        CmsObject cms;
785        if (controller != null) {
786            cms = controller.getCmsObject();
787        } else {
788            cms = (CmsObject)req.getAttribute(ATTRIBUTE_CMS_OBJECT);
789        }
790        if (cms == null) {
791            // cms object unavailable - this request was not initialized properly
792            throw new CmsRuntimeException(
793                Messages.get().container(Messages.ERR_MISSING_CMS_CONTROLLER_1, CmsJspBean.class.getName()));
794        }
795        updateCmsObject(cms);
796
797        m_detailContentResource = CmsDetailPageResourceHandler.getDetailResource(req);
798        m_detailFunctionPage = CmsDetailPageResourceHandler.getDetailFunctionPage(req);
799    }
800
801    /**
802     * Creates a new instance of the standard JSP context bean.<p>
803     *
804     * To prevent multiple creations of the bean during a request, the OpenCms request context
805     * attributes are used to cache the created VFS access utility bean.<p>
806     *
807     * @param req the current servlet request
808     *
809     * @return a new instance of the standard JSP context bean
810     */
811    public static CmsJspStandardContextBean getInstance(ServletRequest req) {
812
813        Object attribute = req.getAttribute(ATTRIBUTE_NAME);
814        CmsJspStandardContextBean result;
815        if ((attribute != null) && (attribute instanceof CmsJspStandardContextBean)) {
816            result = (CmsJspStandardContextBean)attribute;
817        } else {
818            result = new CmsJspStandardContextBean(req);
819            req.setAttribute(ATTRIBUTE_NAME, result);
820        }
821        return result;
822    }
823
824    /**
825     * Returns a copy of this JSP context bean.<p>
826     *
827     * @return a copy of this JSP context bean
828     */
829    public CmsJspStandardContextBean createCopy() {
830
831        CmsJspStandardContextBean result = new CmsJspStandardContextBean();
832        result.m_container = m_container;
833        if (m_detailContentResource != null) {
834            result.m_detailContentResource = m_detailContentResource.getCopy();
835        }
836        result.m_element = m_element;
837        result.setPage(m_page);
838        return result;
839    }
840
841    /**
842     * Returns a caching hash specific to the element, it's properties and the current container width.<p>
843     *
844     * @return the caching hash
845     */
846    public String elementCachingHash() {
847
848        String result = "";
849        if (m_element != null) {
850            result = m_element.editorHash();
851            if (m_container != null) {
852                result += "w:"
853                    + m_container.getWidth()
854                    + "cName:"
855                    + m_container.getName()
856                    + "cType:"
857                    + m_container.getType();
858            }
859        }
860        return result;
861    }
862
863    /**
864     * Returns the locales available for the currently requested URI.
865     *
866     * @return the locales available for the currently requested URI.
867     */
868    public List<Locale> getAvailableLocales() {
869
870        return OpenCms.getLocaleManager().getAvailableLocales(m_cms, getRequestContext().getUri());
871    }
872
873    /**
874     * Helper for easy instantiation and initialization of custom context beans that returns
875     * an instance of the class specified via <code>className</code>, with the current context already set.
876     *
877     * @param className name of the class to instantiate. Must be a subclass of {@link A_CmsJspCustomContextBean}.
878     * @return an instance of the provided class with the current context already set.
879     */
880    public Object getBean(String className) {
881
882        try {
883            Class<?> clazz = Class.forName(className);
884            if (A_CmsJspCustomContextBean.class.isAssignableFrom(clazz)) {
885                Constructor<?> constructor = clazz.getConstructor();
886                Object instance = constructor.newInstance();
887                Method setContextMethod = clazz.getMethod("setContext", CmsJspStandardContextBean.class);
888                setContextMethod.invoke(instance, this);
889                return instance;
890            } else {
891                throw new Exception();
892            }
893        } catch (Exception e) {
894            LOG.error(Messages.get().container(Messages.ERR_NO_CUSTOM_BEAN_1, className));
895        }
896        return null;
897
898    }
899
900    /**
901     * Returns the container the currently rendered element is part of.<p>
902     *
903     * @return the currently the currently rendered element is part of
904     */
905    public CmsContainerBean getContainer() {
906
907        return m_container;
908    }
909
910    /**
911     * Returns the current detail content, or <code>null</code> if no detail content is requested.<p>
912     *
913     * @return the current detail content, or <code>null</code> if no detail content is requested.<p>
914     */
915    public CmsJspResourceWrapper getDetailContent() {
916
917        return CmsJspResourceWrapper.wrap(m_cms, m_detailContentResource);
918    }
919
920    /**
921     * Returns the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p>
922     *
923     * @return the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p>
924     */
925    public CmsUUID getDetailContentId() {
926
927        return m_detailContentResource == null ? null : m_detailContentResource.getStructureId();
928    }
929
930    /**
931     * Returns the detail content site path, or <code>null</code> if not available.<p>
932     *
933     * @return the detail content site path
934     */
935    public String getDetailContentSitePath() {
936
937        return ((m_cms == null) || (m_detailContentResource == null))
938        ? null
939        : m_cms.getSitePath(m_detailContentResource);
940    }
941
942    /**
943     * Returns the detail function page.<p>
944     *
945     * @return the detai function page
946     */
947    public CmsJspResourceWrapper getDetailFunctionPage() {
948
949        return CmsJspResourceWrapper.wrap(m_cms, m_detailFunctionPage);
950    }
951
952    /**
953     * Returns the detail only page.<p>
954     *
955     * @return the detail only page
956     */
957    public CmsContainerPageBean getDetailOnlyPage() {
958
959        return m_detailOnlyPage;
960    }
961
962    /**
963     * Returns the currently rendered element.<p>
964     *
965     * @return the currently rendered element
966     */
967    public CmsContainerElementWrapper getElement() {
968
969        return m_element != null ? new CmsContainerElementWrapper(m_element) : null;
970    }
971
972    /**
973     * Returns a lazy initialized map of wrapped element resources by container name.<p>
974     *
975     * @return the lazy map of element resource wrappers
976     */
977    public Map<String, List<CmsJspResourceWrapper>> getElementsInContainer() {
978
979        return CmsCollectionsGenericWrapper.createLazyMap(obj -> {
980            if (obj instanceof String) {
981                List<CmsJspResourceWrapper> elements = new ArrayList<>();
982                CmsContainerBean container = getPage().getContainers().get(obj);
983                if (container != null) {
984                    for (CmsContainerElementBean element : container.getElements()) {
985                        try {
986                            element.initResource(m_cms);
987                            elements.add(CmsJspResourceWrapper.wrap(m_cms, element.getResource()));
988                        } catch (Exception e) {
989                            LOG.error(e.getLocalizedMessage(), e);
990                        }
991                    }
992                }
993                return elements;
994            } else {
995                return null;
996            }
997        });
998
999    }
1000
1001    /**
1002     * Returns a lazy initialized map of wrapped element resources by container name suffix.<p>
1003     * So in case there is more than one container where the name end with the given suffix, a joined list of elements is returned.<p>
1004     *
1005     * @return the lazy map of element resource wrappers
1006     */
1007    public Map<String, List<CmsJspResourceWrapper>> getElementsInContainers() {
1008
1009        return CmsCollectionsGenericWrapper.createLazyMap(obj -> {
1010            if (obj instanceof String) {
1011                List<CmsJspResourceWrapper> elements = new ArrayList<>();
1012                for (CmsContainerBean container : getPage().getContainers().values()) {
1013                    if (container.getName().endsWith("-" + obj)) {
1014                        for (CmsContainerElementBean element : container.getElements()) {
1015                            try {
1016                                element.initResource(m_cms);
1017                                elements.add(CmsJspResourceWrapper.wrap(m_cms, element.getResource()));
1018                            } catch (Exception e) {
1019                                LOG.error(e.getLocalizedMessage(), e);
1020                            }
1021                        }
1022                    }
1023                }
1024                return elements;
1025            } else {
1026                return null;
1027            }
1028        });
1029
1030    }
1031
1032    /**
1033     * Alternative method name for getReloadMarker().
1034     *
1035     * @see org.opencms.jsp.util.CmsJspStandardContextBean#getReloadMarker()
1036     *
1037     * @return the reload marker
1038     */
1039    public String getEnableReload() {
1040
1041        return getReloadMarker();
1042    }
1043
1044    /**
1045     * Returns a lazy initialized Map which allows access to the dynamic function beans using the JSP EL.<p>
1046     *
1047     * When given a key, the returned map will look up the corresponding dynamic function bean in the module configuration.<p>
1048     *
1049     * @return a lazy initialized Map which allows access to the dynamic function beans using the JSP EL
1050     */
1051    public Map<String, CmsDynamicFunctionBeanWrapper> getFunction() {
1052
1053        if (m_function == null) {
1054
1055            Transformer transformer = new Transformer() {
1056
1057                @Override
1058                public Object transform(Object input) {
1059
1060                    try {
1061                        CmsDynamicFunctionBean dynamicFunction = readDynamicFunctionBean((String)input);
1062                        CmsDynamicFunctionBeanWrapper wrapper = new CmsDynamicFunctionBeanWrapper(
1063                            m_cms,
1064                            dynamicFunction);
1065                        return wrapper;
1066
1067                    } catch (CmsException e) {
1068                        LOG.debug(e.getLocalizedMessage(), e);
1069                        return new CmsDynamicFunctionBeanWrapper(m_cms, null);
1070                    }
1071                }
1072            };
1073            m_function = CmsCollectionsGenericWrapper.createLazyMap(transformer);
1074        }
1075        return m_function;
1076
1077    }
1078
1079    /**
1080     * Deprecated method to access function detail pages using the EL.<p>
1081     *
1082     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
1083     * (named) dynamic function as a key
1084     *
1085     * @deprecated use {@link #getFunctionDetailPage()} instead
1086     */
1087    @Deprecated
1088    public Map<String, String> getFunctionDetail() {
1089
1090        return getFunctionDetailPage();
1091    }
1092
1093    /**
1094     * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a
1095     * (named) dynamic function as a key.<p>
1096     *
1097     * The provided Map key is assumed to be a String that represents a named dynamic function.<p>
1098     *
1099     * Usage example on a JSP with the JSTL:<pre>
1100     * &lt;a href=${cms.functionDetailPage['search']} /&gt
1101     * </pre>
1102     *
1103     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
1104     * (named) dynamic function as a key
1105     *
1106     * @see #getTypeDetailPage()
1107     */
1108    public Map<String, String> getFunctionDetailPage() {
1109
1110        if (m_functionDetailPage == null) {
1111            m_functionDetailPage = CmsCollectionsGenericWrapper.createLazyMap(
1112                new CmsDetailLookupTransformer(CmsDetailPageInfo.FUNCTION_PREFIX));
1113        }
1114        return m_functionDetailPage;
1115    }
1116
1117    /**
1118     * Returns a lazy map which creates a wrapper object for a dynamic function format when given an XML content
1119     * as a key.<p>
1120     *
1121     * @return a lazy map for accessing function formats for a content
1122     */
1123    public Map<CmsJspContentAccessBean, CmsDynamicFunctionFormatWrapper> getFunctionFormatFromContent() {
1124
1125        Transformer transformer = new Transformer() {
1126
1127            @Override
1128            public Object transform(Object contentAccess) {
1129
1130                CmsXmlContent content = (CmsXmlContent)(((CmsJspContentAccessBean)contentAccess).getRawContent());
1131                CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser();
1132                CmsDynamicFunctionBean functionBean = null;
1133                try {
1134                    functionBean = parser.parseFunctionBean(m_cms, content);
1135                } catch (CmsException e) {
1136                    LOG.debug(e.getLocalizedMessage(), e);
1137                    return new CmsDynamicFunctionFormatWrapper(m_cms, null);
1138                }
1139                String type = getContainer().getType();
1140                String width = getContainer().getWidth();
1141                int widthNum = -1;
1142                try {
1143                    widthNum = Integer.parseInt(width);
1144                } catch (NumberFormatException e) {
1145                    LOG.debug(e.getLocalizedMessage(), e);
1146                }
1147                CmsDynamicFunctionBean.Format format = functionBean.getFormatForContainer(m_cms, type, widthNum);
1148                CmsDynamicFunctionFormatWrapper wrapper = new CmsDynamicFunctionFormatWrapper(m_cms, format);
1149                return wrapper;
1150            }
1151        };
1152        return CmsCollectionsGenericWrapper.createLazyMap(transformer);
1153    }
1154
1155    /**
1156     * Checks if the current request should be direct edit enabled.
1157     * Online-, history-requests, previews and temporary files will not be editable.<p>
1158     *
1159     * @return <code>true</code> if the current request should be direct edit enabled
1160     */
1161    public boolean getIsEditMode() {
1162
1163        if (m_isEditMode == null) {
1164            m_isEditMode = Boolean.valueOf(CmsJspTagEditable.isEditableRequest(m_request));
1165        }
1166        return m_isEditMode.booleanValue();
1167    }
1168
1169    /**
1170     * Returns true if the current request is a JSON request.<p>
1171     *
1172     * @return true if we are in a JSON request
1173     */
1174    public boolean getIsJSONRequest() {
1175
1176        return CmsJsonPartFilter.isJsonRequest(m_request);
1177    }
1178
1179    /**
1180     * Returns if the current project is the online project.<p>
1181     *
1182     * @return <code>true</code> if the current project is the online project
1183     */
1184    public boolean getIsOnlineProject() {
1185
1186        return m_cms.getRequestContext().getCurrentProject().isOnlineProject();
1187    }
1188
1189    /**
1190     * Returns the current locale.<p>
1191     *
1192     * @return the current locale
1193     */
1194    public Locale getLocale() {
1195
1196        return getRequestContext().getLocale();
1197    }
1198
1199    /**
1200     * Gets a map providing access to the locale variants of the current page.<p>
1201     *
1202     * Note that all available locales for the site / subsite are used as keys, not just the ones for which a locale
1203     * variant actually exists.
1204     *
1205     * Usage in JSPs: ${cms.localeResource['de']]
1206     *
1207     * @return the map from locale strings to locale variant resources
1208     */
1209    public Map<String, CmsJspResourceWrapper> getLocaleResource() {
1210
1211        Map<String, CmsJspResourceWrapper> result = getPageResource().getLocaleResource();
1212        List<Locale> locales = CmsLocaleGroupService.getPossibleLocales(m_cms, getPageResource());
1213        for (Locale locale : locales) {
1214            if (!result.containsKey(locale.toString())) {
1215                result.put(locale.toString(), null);
1216            }
1217        }
1218        return result;
1219    }
1220
1221    /**
1222     * Gets the main locale for the current page's locale group.<p>
1223     *
1224     * @return the main locale for the current page's locale group
1225     */
1226    public Locale getMainLocale() {
1227
1228        return getPageResource().getMainLocale();
1229    }
1230
1231    /**
1232     * Returns the meta mappings map.<p>
1233     *
1234     * @return the meta mappings
1235     */
1236    public Map<String, String> getMeta() {
1237
1238        initMetaMappings();
1239        return CmsCollectionsGenericWrapper.createLazyMap(new MetaLookupTranformer());
1240    }
1241
1242    /**
1243     * Returns the currently displayed container page.<p>
1244     *
1245     * @return the currently displayed container page
1246     */
1247    public CmsContainerPageBean getPage() {
1248
1249        return m_page;
1250    }
1251
1252    /**
1253     * Returns the container page bean for the give page and locale.<p>
1254     *
1255     * @param page the container page resource as id, path or already as resource
1256     * @param locale the content locale as locale or string
1257     *
1258     * @return the container page bean
1259     */
1260    public CmsContainerPageBean getPage(Object page, Object locale) {
1261
1262        CmsResource pageResource = null;
1263        CmsContainerPageBean result = null;
1264        if (m_cms != null) {
1265            try {
1266                pageResource = CmsJspElFunctions.convertRawResource(m_cms, page);
1267                Locale l = CmsJspElFunctions.convertLocale(locale);
1268                result = getPage(pageResource);
1269                if (result != null) {
1270                    CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(
1271                        m_cms,
1272                        pageResource.getRootPath());
1273                    for (CmsContainerBean container : result.getContainers().values()) {
1274                        for (CmsContainerElementBean element : container.getElements()) {
1275                            boolean isGroupContainer = element.isGroupContainer(m_cms);
1276                            boolean isInheritedContainer = element.isInheritedContainer(m_cms);
1277                            I_CmsFormatterBean formatterConfig = null;
1278                            if (!isGroupContainer && !isInheritedContainer) {
1279                                element.initResource(m_cms);
1280                                // ensure that the formatter configuration id is added to the element settings, so it will be persisted on save
1281                                formatterConfig = CmsJspTagContainer.getFormatterConfigurationForElement(
1282                                    m_cms,
1283                                    element,
1284                                    adeConfig,
1285                                    container.getName(),
1286                                    "",
1287                                    0);
1288                                if (formatterConfig != null) {
1289                                    element.initSettings(m_cms, formatterConfig, l, m_request, null);
1290                                }
1291                            }
1292                        }
1293                    }
1294                }
1295            } catch (Exception e) {
1296                LOG.warn(e.getLocalizedMessage(), e);
1297            }
1298
1299        }
1300        return result;
1301    }
1302
1303    /**
1304     * Returns the current container page resource.<p>
1305     *
1306     * @return the current container page resource
1307     */
1308    public CmsJspResourceWrapper getPageResource() {
1309
1310        try {
1311            if (m_pageResource == null) {
1312                // get the container page itself, checking the history first
1313                m_pageResource = CmsJspResourceWrapper.wrap(
1314                    m_cms,
1315                    (CmsResource)CmsHistoryResourceHandler.getHistoryResource(m_request));
1316                if (m_pageResource == null) {
1317                    m_pageResource = CmsJspResourceWrapper.wrap(
1318                        m_cms,
1319                        m_cms.readResource(m_cms.getRequestContext().getUri()));
1320                }
1321            }
1322        } catch (CmsException e) {
1323            LOG.error(e.getLocalizedMessage(), e);
1324        }
1325        return m_pageResource;
1326    }
1327
1328    /**
1329     * Returns the parent container to the current container if available.<p>
1330     *
1331     * @return the parent container
1332     */
1333    public CmsContainerBean getParentContainer() {
1334
1335        CmsContainerBean result = null;
1336        if ((getContainer() != null) && (getContainer().getParentInstanceId() != null)) {
1337            result = m_parentContainers.get(getContainer().getParentInstanceId());
1338        }
1339        return result;
1340    }
1341
1342    /**
1343     * Returns the instance id parent container mapping.<p>
1344     *
1345     * @return the instance id parent container mapping
1346     */
1347    public Map<String, CmsContainerBean> getParentContainers() {
1348
1349        if (m_parentContainers == null) {
1350            initPageData();
1351        }
1352        return Collections.unmodifiableMap(m_parentContainers);
1353    }
1354
1355    /**
1356     * Returns the parent element to the current element if available.<p>
1357     *
1358     * @return the parent element or null
1359     */
1360    public CmsContainerElementBean getParentElement() {
1361
1362        return getParentElement(getElement());
1363    }
1364
1365    /**
1366     * JSP EL accessor method for retrieving the preview formatters.<p>
1367     *
1368     * @return a lazy map for accessing preview formatters
1369     */
1370    public Map<String, String> getPreviewFormatter() {
1371
1372        Transformer transformer = new Transformer() {
1373
1374            @Override
1375            public Object transform(Object uri) {
1376
1377                try {
1378                    String rootPath = m_cms.getRequestContext().addSiteRoot((String)uri);
1379                    CmsResource resource = m_cms.readResource((String)uri);
1380                    CmsADEManager adeManager = OpenCms.getADEManager();
1381                    CmsADEConfigData configData = adeManager.lookupConfiguration(m_cms, rootPath);
1382                    CmsFormatterConfiguration formatterConfig = configData.getFormatters(m_cms, resource);
1383                    if (formatterConfig == null) {
1384                        return "";
1385                    }
1386                    I_CmsFormatterBean previewFormatter = formatterConfig.getPreviewFormatter();
1387                    if (previewFormatter == null) {
1388                        return "";
1389                    }
1390                    CmsUUID structureId = previewFormatter.getJspStructureId();
1391                    m_cms.readResource(structureId);
1392                    CmsResource formatterResource = m_cms.readResource(structureId);
1393                    String formatterSitePath = m_cms.getRequestContext().removeSiteRoot(
1394                        formatterResource.getRootPath());
1395                    return formatterSitePath;
1396                } catch (CmsException e) {
1397                    LOG.warn(e.getLocalizedMessage(), e);
1398                    return "";
1399                }
1400            }
1401        };
1402        return CmsCollectionsGenericWrapper.createLazyMap(transformer);
1403    }
1404
1405    /**
1406     * Reads all sub-categories below the provided category.
1407     * @return The map from the provided category to it's sub-categories in a {@link CmsJspCategoryAccessBean}.
1408     */
1409    public Map<String, CmsJspCategoryAccessBean> getReadAllSubCategories() {
1410
1411        if (null == m_allSubCategories) {
1412            m_allSubCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1413
1414                @Override
1415                public Object transform(Object categoryPath) {
1416
1417                    try {
1418                        List<CmsCategory> categories = CmsCategoryService.getInstance().readCategories(
1419                            m_cms,
1420                            (String)categoryPath,
1421                            true,
1422                            m_cms.getRequestContext().getUri());
1423                        CmsJspCategoryAccessBean result = new CmsJspCategoryAccessBean(
1424                            m_cms,
1425                            categories,
1426                            (String)categoryPath);
1427                        return result;
1428                    } catch (CmsException e) {
1429                        LOG.warn(e.getLocalizedMessage(), e);
1430                        return null;
1431                    }
1432                }
1433
1434            });
1435        }
1436        return m_allSubCategories;
1437    }
1438
1439    /**
1440     * Reads the categories assigned to the currently requested URI.
1441     * @return the categories assigned to the currently requested URI.
1442     */
1443    public CmsJspCategoryAccessBean getReadCategories() {
1444
1445        return getReadResourceCategories().get(getRequestContext().getRootUri());
1446    }
1447
1448    /**
1449     * Transforms the category path of a category to the category.
1450     * @return a map from root or site path to category.
1451     */
1452    public Map<String, CmsCategory> getReadCategory() {
1453
1454        if (null == m_categories) {
1455            m_categories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1456
1457                public Object transform(Object categoryPath) {
1458
1459                    try {
1460                        CmsCategoryService catService = CmsCategoryService.getInstance();
1461                        return catService.localizeCategory(
1462                            m_cms,
1463                            catService.readCategory(m_cms, (String)categoryPath, getRequestContext().getUri()),
1464                            m_cms.getRequestContext().getLocale());
1465                    } catch (CmsException e) {
1466                        LOG.warn(e.getLocalizedMessage(), e);
1467                        return null;
1468                    }
1469                }
1470
1471            });
1472        }
1473        return m_categories;
1474    }
1475
1476    /**
1477     * Transforms the category path to the list of all categories on that path.<p>
1478     *
1479     * Example: For path <code>"location/europe/"</code>
1480     *          the list <code>[getReadCategory.get("location/"),getReadCategory.get("location/europe/")]</code>
1481     *          is returned.
1482     * @return a map from a category path to list of categories on that path.
1483     */
1484    public Map<String, List<CmsCategory>> getReadPathCategories() {
1485
1486        if (null == m_pathCategories) {
1487            m_pathCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1488
1489                public Object transform(Object categoryPath) {
1490
1491                    List<CmsCategory> result = new ArrayList<CmsCategory>();
1492
1493                    String path = (String)categoryPath;
1494
1495                    if ((null == path) || (path.length() <= 1)) {
1496                        return result;
1497                    }
1498
1499                    //cut last slash
1500                    path = path.substring(0, path.length() - 1);
1501
1502                    List<String> pathParts = Arrays.asList(path.split("/"));
1503
1504                    String currentPath = "";
1505                    for (String part : pathParts) {
1506                        currentPath += part + "/";
1507                        CmsCategory category = getReadCategory().get(currentPath);
1508                        if (null != category) {
1509                            result.add(category);
1510                        }
1511                    }
1512                    return CmsCategoryService.getInstance().localizeCategories(
1513                        m_cms,
1514                        result,
1515                        m_cms.getRequestContext().getLocale());
1516                }
1517
1518            });
1519        }
1520        return m_pathCategories;
1521    }
1522
1523    /**
1524     * Reads the categories assigned to a resource.
1525     *
1526     * @return map from the resource path (root path) to the assigned categories
1527     */
1528    public Map<String, CmsJspCategoryAccessBean> getReadResourceCategories() {
1529
1530        if (null == m_resourceCategories) {
1531            m_resourceCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1532
1533                public Object transform(Object resourceName) {
1534
1535                    try {
1536                        CmsResource resource = m_cms.readResource(
1537                            getRequestContext().removeSiteRoot((String)resourceName));
1538                        return new CmsJspCategoryAccessBean(m_cms, resource);
1539                    } catch (CmsException e) {
1540                        LOG.warn(e.getLocalizedMessage(), e);
1541                        return null;
1542                    }
1543                }
1544            });
1545        }
1546        return m_resourceCategories;
1547    }
1548
1549    /**
1550     * Returns a HTML comment string that will cause the container page editor to reload the page if the element or its settings
1551     * were edited.<p>
1552     *
1553     * @return the reload marker
1554     */
1555    public String getReloadMarker() {
1556
1557        if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) {
1558            return ""; // reload marker is not needed in Online mode
1559        } else {
1560            return CmsGwtConstants.FORMATTER_RELOAD_MARKER;
1561        }
1562    }
1563
1564    /**
1565     * Returns the request context.<p>
1566     *
1567     * @return the request context
1568     */
1569    public CmsRequestContext getRequestContext() {
1570
1571        return m_cms.getRequestContext();
1572    }
1573
1574    /**
1575     * Returns the current site.<p>
1576     *
1577     * @return the current site
1578     */
1579    public CmsSite getSite() {
1580
1581        return OpenCms.getSiteManager().getSiteForSiteRoot(m_cms.getRequestContext().getSiteRoot());
1582    }
1583
1584    /**
1585     * Transforms root paths to site paths.
1586     *
1587     * @return lazy map from root paths to site paths.
1588     *
1589     * @see CmsRequestContext#removeSiteRoot(String)
1590     */
1591    public Map<String, String> getSitePath() {
1592
1593        if (m_sitePaths == null) {
1594            m_sitePaths = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1595
1596                public Object transform(Object rootPath) {
1597
1598                    if (rootPath instanceof String) {
1599                        return getRequestContext().removeSiteRoot((String)rootPath);
1600                    }
1601                    return null;
1602                }
1603            });
1604        }
1605        return m_sitePaths;
1606    }
1607
1608    /**
1609     * Returns the subsite path for the currently requested URI.<p>
1610     *
1611     * @return the subsite path
1612     */
1613    public String getSubSitePath() {
1614
1615        return m_cms.getRequestContext().removeSiteRoot(
1616            OpenCms.getADEManager().getSubSiteRoot(m_cms, m_cms.getRequestContext().getRootUri()));
1617    }
1618
1619    /**
1620     * Returns the system information.<p>
1621     *
1622     * @return the system information
1623     */
1624    public CmsSystemInfo getSystemInfo() {
1625
1626        return OpenCms.getSystemInfo();
1627    }
1628
1629    /**
1630     * Gets a bean containing information about the current template.<p>
1631     *
1632     * @return the template information bean
1633     */
1634    public TemplateBean getTemplate() {
1635
1636        TemplateBean templateBean = getRequestAttribute(CmsTemplateContextManager.ATTR_TEMPLATE_BEAN);
1637        if (templateBean == null) {
1638            templateBean = new TemplateBean("", "");
1639        }
1640        return templateBean;
1641    }
1642
1643    /**
1644     * Returns the title of a page delivered from OpenCms, usually used for the <code>&lt;title&gt;</code> tag of
1645     * a HTML page.<p>
1646     *
1647     * If no title information has been found, the empty String "" is returned.<p>
1648     *
1649     * @return the title of the current page
1650     */
1651    public String getTitle() {
1652
1653        return getLocaleSpecificTitle(null);
1654
1655    }
1656
1657    /**
1658     * Get the title and read the Title property according the provided locale.
1659     * @return The map from locales to the locale specific titles.
1660     */
1661    public Map<String, String> getTitleLocale() {
1662
1663        if (m_localeTitles == null) {
1664            m_localeTitles = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1665
1666                public Object transform(Object inputLocale) {
1667
1668                    Locale locale = null;
1669                    if (null != inputLocale) {
1670                        if (inputLocale instanceof Locale) {
1671                            locale = (Locale)inputLocale;
1672                        } else if (inputLocale instanceof String) {
1673                            try {
1674                                locale = LocaleUtils.toLocale((String)inputLocale);
1675                            } catch (IllegalArgumentException | NullPointerException e) {
1676                                // do nothing, just go on without locale
1677                            }
1678                        }
1679                    }
1680                    return getLocaleSpecificTitle(locale);
1681                }
1682
1683            });
1684        }
1685        return m_localeTitles;
1686    }
1687
1688    /**
1689     * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a
1690     * resource type as a key.<p>
1691     *
1692     * The provided Map key is assumed to be the name of a resource type that has a detail page configured.<p>
1693     *
1694     * Usage example on a JSP with the JSTL:<pre>
1695     * &lt;a href=${cms.typeDetailPage['bs-blog']} /&gt
1696     * </pre>
1697     *
1698     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
1699     * resource type as a key
1700     *
1701     * @see #getFunctionDetailPage()
1702     */
1703    public Map<String, String> getTypeDetailPage() {
1704
1705        if (m_typeDetailPage == null) {
1706            m_typeDetailPage = CmsCollectionsGenericWrapper.createLazyMap(new CmsDetailLookupTransformer(""));
1707        }
1708        return m_typeDetailPage;
1709    }
1710
1711    /**
1712     * Returns an initialized VFS access bean.<p>
1713     *
1714     * @return an initialized VFS access bean
1715     */
1716    public CmsJspVfsAccessBean getVfs() {
1717
1718        if (m_vfsBean == null) {
1719            // create a new VVFS access bean
1720            m_vfsBean = CmsJspVfsAccessBean.create(m_cms);
1721        }
1722        return m_vfsBean;
1723    }
1724
1725    /**
1726     * Returns the workplace locale from the current user's settings.<p>
1727     *
1728     * @return returns the workplace locale from the current user's settings
1729     */
1730    public Locale getWorkplaceLocale() {
1731
1732        return OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
1733    }
1734
1735    /**
1736     * Returns an EL access wrapper map for the given object.<p>
1737     *
1738     * If the object is a {@link CmsResource}, then a {@link CmsJspResourceWrapper} is returned.
1739     * Otherwise the object is wrapped in a {@link CmsJspObjectValueWrapper}.<p>
1740     *
1741     * If the object is already is a wrapper, it is returned unchanged.<p>
1742     *
1743     * @return an EL access wrapper map for the given object
1744     */
1745    public Map<Object, Object> getWrap() {
1746
1747        return CmsCollectionsGenericWrapper.createLazyMap(obj -> {
1748
1749            if ((obj instanceof A_CmsJspValueWrapper) || (obj instanceof CmsJspResourceWrapper)) {
1750                return obj;
1751            } else if (obj instanceof CmsResource) {
1752                return CmsJspResourceWrapper.wrap(m_cms, (CmsResource)obj);
1753            } else {
1754                return CmsJspObjectValueWrapper.createWrapper(m_cms, obj);
1755            }
1756        });
1757    }
1758
1759    /**
1760     * Initializes the requested container page.<p>
1761     *
1762     * @throws CmsException in case reading the requested resource fails
1763     */
1764    public void initPage() throws CmsException {
1765
1766        if ((m_page == null) && (m_cms != null)) {
1767            String requestUri = m_cms.getRequestContext().getUri();
1768            // get the container page itself, checking the history first
1769            CmsResource pageResource = (CmsResource)CmsHistoryResourceHandler.getHistoryResource(m_request);
1770            if (pageResource == null) {
1771                pageResource = m_cms.readResource(requestUri);
1772            }
1773            m_page = getPage(pageResource);
1774            m_page = CmsTemplateMapper.get(m_request).transformContainerpageBean(
1775                m_cms,
1776                m_page,
1777                pageResource.getRootPath());
1778
1779        }
1780    }
1781
1782    /**
1783     * Returns <code>true</code in case a detail page is available for the current element.<p>
1784     *
1785     * @return <code>true</code in case a detail page is available for the current element
1786     */
1787    public boolean isDetailPageAvailable() {
1788
1789        boolean result = false;
1790        if ((m_cms != null)
1791            && (m_element != null)
1792            && !m_element.isInMemoryOnly()
1793            && (m_element.getResource() != null)) {
1794            try {
1795                String detailPage = OpenCms.getADEManager().getDetailPageFinder().getDetailPage(
1796                    m_cms,
1797                    m_element.getResource().getRootPath(),
1798                    m_cms.getRequestContext().getUri(),
1799                    null);
1800                result = detailPage != null;
1801            } catch (CmsException e) {
1802                LOG.warn(e.getLocalizedMessage(), e);
1803            }
1804        }
1805        return result;
1806    }
1807
1808    /**
1809     * Returns <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise.<p>
1810     *
1811     * Same as to check if {@link #getDetailContent()} is <code>null</code>.<p>
1812     *
1813     * @return <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise
1814     */
1815    public boolean isDetailRequest() {
1816
1817        return m_detailContentResource != null;
1818    }
1819
1820    /**
1821     * Returns if the page is in drag mode.<p>
1822     *
1823     * @return if the page is in drag mode
1824     */
1825    public boolean isDragMode() {
1826
1827        return m_isDragMode;
1828    }
1829
1830    /**
1831     * Returns the flag to indicate if in drag and drop mode.<p>
1832     *
1833     * @return <code>true</code> if in drag and drop mode
1834     */
1835    public boolean isEdited() {
1836
1837        return m_edited;
1838    }
1839
1840    /**
1841     * Returns if the current element is a model group.<p>
1842     *
1843     * @return <code>true</code> if the current element is a model group
1844     */
1845    public boolean isModelGroupElement() {
1846
1847        return (m_element != null) && !m_element.isInMemoryOnly() && isModelGroupPage() && m_element.isModelGroup();
1848    }
1849
1850    /**
1851     * Returns if the current page is used to manage model groups.<p>
1852     *
1853     * @return <code>true</code> if the current page is used to manage model groups
1854     */
1855    public boolean isModelGroupPage() {
1856
1857        CmsResource page = getPageResource();
1858        return (page != null) && CmsContainerpageService.isEditingModelGroups(m_cms, page);
1859
1860    }
1861
1862    /**
1863     * Sets the container the currently rendered element is part of.<p>
1864     *
1865     * @param container the container the currently rendered element is part of
1866     */
1867    public void setContainer(CmsContainerBean container) {
1868
1869        m_container = container;
1870    }
1871
1872    /**
1873     * Sets the detail only page.<p>
1874     *
1875     * @param detailOnlyPage the detail only page to set
1876     */
1877    public void setDetailOnlyPage(CmsContainerPageBean detailOnlyPage) {
1878
1879        m_detailOnlyPage = detailOnlyPage;
1880        clearPageData();
1881    }
1882
1883    /**
1884     * Sets if the page is in drag mode.<p>
1885     *
1886     * @param isDragMode if the page is in drag mode
1887     */
1888    public void setDragMode(boolean isDragMode) {
1889
1890        m_isDragMode = isDragMode;
1891    }
1892
1893    /**
1894     * Sets the flag to indicate if in drag and drop mode.<p>
1895     *
1896     * @param edited <code>true</code> if in drag and drop mode
1897     */
1898    public void setEdited(boolean edited) {
1899
1900        m_edited = edited;
1901    }
1902
1903    /**
1904     * Sets the currently rendered element.<p>
1905     *
1906     * @param element the currently rendered element to set
1907     */
1908    public void setElement(CmsContainerElementBean element) {
1909
1910        m_element = element;
1911    }
1912
1913    /**
1914     * Sets the currently displayed container page.<p>
1915     *
1916     * @param page the currently displayed container page to set
1917     */
1918    public void setPage(CmsContainerPageBean page) {
1919
1920        m_page = page;
1921        clearPageData();
1922    }
1923
1924    /**
1925     * Updates the internally stored OpenCms user context.<p>
1926     *
1927     * @param cms the new OpenCms user context
1928     */
1929    public void updateCmsObject(CmsObject cms) {
1930
1931        try {
1932            m_cms = OpenCms.initCmsObject(cms);
1933        } catch (CmsException e) {
1934            LOG.error(e.getLocalizedMessage(), e);
1935            m_cms = cms;
1936        }
1937    }
1938
1939    /**
1940     * Updates the standard context bean from the request.<p>
1941     *
1942     * @param cmsFlexRequest the request from which to update the data
1943     */
1944    public void updateRequestData(CmsFlexRequest cmsFlexRequest) {
1945
1946        CmsResource detailRes = CmsDetailPageResourceHandler.getDetailResource(cmsFlexRequest);
1947        m_detailContentResource = detailRes;
1948        m_request = cmsFlexRequest;
1949
1950    }
1951
1952    /**
1953     * Returns the formatter configuration to the given element.<p>
1954     *
1955     * @param element the element
1956     *
1957     * @return the formatter configuration
1958     */
1959    protected I_CmsFormatterBean getElementFormatter(CmsContainerElementBean element) {
1960
1961        if (m_elementInstances == null) {
1962            initPageData();
1963        }
1964        I_CmsFormatterBean formatter = null;
1965        CmsContainerBean container = m_parentContainers.get(element.getInstanceId());
1966        if (container == null) {
1967            // use the current container
1968            container = getContainer();
1969        }
1970        if (container != null) {
1971            String containerName = container.getName();
1972            Map<String, String> settings = element.getSettings();
1973            if (settings != null) {
1974                String formatterConfigId = settings.get(CmsFormatterConfig.getSettingsKeyForContainer(containerName));
1975                if (CmsUUID.isValidUUID(formatterConfigId)) {
1976                    formatter = OpenCms.getADEManager().getCachedFormatters(false).getFormatters().get(
1977                        new CmsUUID(formatterConfigId));
1978                }
1979            }
1980            if (formatter == null) {
1981                try {
1982                    CmsResource resource = m_cms.readResource(m_cms.getRequestContext().getUri());
1983
1984                    CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
1985                        m_cms,
1986                        resource.getRootPath());
1987                    CmsFormatterConfiguration formatters = config.getFormatters(m_cms, resource);
1988                    int width = -2;
1989                    try {
1990                        width = Integer.parseInt(container.getWidth());
1991                    } catch (Exception e) {
1992                        LOG.debug(e.getLocalizedMessage(), e);
1993                    }
1994                    formatter = formatters.getDefaultSchemaFormatter(container.getType(), width);
1995                } catch (CmsException e1) {
1996                    if (LOG.isWarnEnabled()) {
1997                        LOG.warn(e1.getLocalizedMessage(), e1);
1998                    }
1999                }
2000            }
2001        }
2002        return formatter;
2003    }
2004
2005    /**
2006     * Returns the title according to the given locale.
2007     * @param locale the locale for which the title should be read.
2008     * @return the title according to the given locale
2009     */
2010    protected String getLocaleSpecificTitle(Locale locale) {
2011
2012        String result = null;
2013
2014        try {
2015
2016            if (isDetailRequest()) {
2017                // this is a request to a detail page
2018                CmsResource res = getDetailContent();
2019                CmsFile file = m_cms.readFile(res);
2020                CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, file);
2021                result = content.getHandler().getTitleMapping(m_cms, content, m_cms.getRequestContext().getLocale());
2022                if (result == null) {
2023                    // title not found, maybe no mapping OR not available in the current locale
2024                    // read the title of the detail resource as fall back (may contain mapping from another locale)
2025                    result = m_cms.readPropertyObject(
2026                        res,
2027                        CmsPropertyDefinition.PROPERTY_TITLE,
2028                        false,
2029                        locale).getValue();
2030                }
2031            }
2032            if (result == null) {
2033                // read the title of the requested resource as fall back
2034                result = m_cms.readPropertyObject(
2035                    m_cms.getRequestContext().getUri(),
2036                    CmsPropertyDefinition.PROPERTY_TITLE,
2037                    true,
2038                    locale).getValue();
2039            }
2040        } catch (CmsException e) {
2041            LOG.debug(e.getLocalizedMessage(), e);
2042        }
2043        if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) {
2044            result = "";
2045        }
2046
2047        return result;
2048    }
2049
2050    /**
2051     * Returns the parent element if available.<p>
2052     *
2053     * @param element the element
2054     *
2055     * @return the parent element or null
2056     */
2057    protected CmsContainerElementBean getParentElement(CmsContainerElementBean element) {
2058
2059        if (m_elementInstances == null) {
2060            initPageData();
2061        }
2062        CmsContainerElementBean parent = null;
2063        CmsContainerBean cont = m_parentContainers.get(element.getInstanceId());
2064        if ((cont != null) && cont.isNestedContainer()) {
2065            parent = m_elementInstances.get(cont.getParentInstanceId());
2066        }
2067        return parent;
2068    }
2069
2070    /**
2071     * Reads a dynamic function bean, given its name in the module configuration.<p>
2072     *
2073     * @param configuredName the name of the dynamic function in the module configuration
2074     * @return the dynamic function bean for the dynamic function configured under that name
2075     *
2076     * @throws CmsException if something goes wrong
2077     */
2078    protected CmsDynamicFunctionBean readDynamicFunctionBean(String configuredName) throws CmsException {
2079
2080        CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
2081            m_cms,
2082            m_cms.addSiteRoot(m_cms.getRequestContext().getUri()));
2083        CmsFunctionReference functionRef = config.getFunctionReference(configuredName);
2084        if (functionRef == null) {
2085            return null;
2086        }
2087        CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser();
2088        CmsResource functionResource = m_cms.readResource(functionRef.getStructureId());
2089        CmsDynamicFunctionBean result = parser.parseFunctionBean(m_cms, functionResource);
2090        return result;
2091    }
2092
2093    /**
2094     * Adds the mappings of the given formatter configuration.<p>
2095     *
2096     * @param formatterBean the formatter bean
2097     * @param elementId the element content structure id
2098     * @param resolver The macro resolver used on key and default value of the mappings
2099     * @param isDetailContent in case of a detail content
2100     */
2101    private void addMappingsForFormatter(
2102        I_CmsFormatterBean formatterBean,
2103        CmsUUID elementId,
2104        CmsMacroResolver resolver,
2105        boolean isDetailContent) {
2106
2107        if ((formatterBean != null) && (formatterBean.getMetaMappings() != null)) {
2108            for (CmsMetaMapping map : formatterBean.getMetaMappings()) {
2109                String key = map.getKey();
2110                key = resolver.resolveMacros(key);
2111                // the detail content mapping overrides other mappings
2112                if (isDetailContent
2113                    || !m_metaMappings.containsKey(key)
2114                    || (m_metaMappings.get(key).m_order <= map.getOrder())) {
2115                    MetaMapping mapping = new MetaMapping();
2116                    mapping.m_key = key;
2117                    mapping.m_elementXPath = map.getElement();
2118                    mapping.m_defaultValue = resolver.resolveMacros(map.getDefaultValue());
2119                    mapping.m_order = map.getOrder();
2120                    mapping.m_contentId = elementId;
2121                    m_metaMappings.put(key, mapping);
2122                }
2123            }
2124        }
2125    }
2126
2127    /**
2128     * Clears the page element data.<p>
2129     */
2130    private void clearPageData() {
2131
2132        m_elementInstances = null;
2133        m_parentContainers = null;
2134    }
2135
2136    /**
2137     * Returns the container page bean for the give resource.<p>
2138     *
2139     * @param pageResource the resource
2140     *
2141     * @return the container page bean
2142     *
2143     * @throws CmsException in case reading the page bean fails
2144     */
2145    private CmsContainerPageBean getPage(CmsResource pageResource) throws CmsException {
2146
2147        CmsContainerPageBean result = null;
2148        if ((pageResource != null) && CmsResourceTypeXmlContainerPage.isContainerPage(pageResource)) {
2149            CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(m_cms, pageResource, m_request);
2150            result = xmlContainerPage.getContainerPage(m_cms);
2151            CmsModelGroupHelper modelHelper = new CmsModelGroupHelper(
2152                m_cms,
2153                OpenCms.getADEManager().lookupConfiguration(m_cms, pageResource.getRootPath()),
2154                CmsJspTagEditable.isEditableRequest(m_request) && (m_request instanceof HttpServletRequest)
2155                ? CmsADESessionCache.getCache((HttpServletRequest)m_request, m_cms)
2156                : null,
2157                CmsContainerpageService.isEditingModelGroups(m_cms, pageResource));
2158            result = modelHelper.readModelGroups(xmlContainerPage.getContainerPage(m_cms));
2159        }
2160        return result;
2161    }
2162
2163    /**
2164     * Convenience method for getting a request attribute without an explicit cast.<p>
2165     *
2166     * @param name the attribute name
2167     * @return the request attribute
2168     */
2169    @SuppressWarnings("unchecked")
2170    private <A> A getRequestAttribute(String name) {
2171
2172        Object attribute = m_request.getAttribute(name);
2173
2174        return attribute != null ? (A)attribute : null;
2175    }
2176
2177    /**
2178     * Initializes the mapping configuration.<p>
2179     */
2180    private void initMetaMappings() {
2181
2182        if (m_metaMappings == null) {
2183            m_metaMappings = new HashMap<String, MetaMapping>();
2184            try {
2185                initPage();
2186                CmsMacroResolver resolver = new CmsMacroResolver();
2187                resolver.setKeepEmptyMacros(true);
2188                resolver.setCmsObject(m_cms);
2189                resolver.setMessages(OpenCms.getWorkplaceManager().getMessages(getLocale()));
2190                CmsResourceFilter filter = getIsEditMode()
2191                ? CmsResourceFilter.IGNORE_EXPIRATION
2192                : CmsResourceFilter.DEFAULT;
2193                for (CmsContainerBean container : m_page.getContainers().values()) {
2194                    for (CmsContainerElementBean element : container.getElements()) {
2195                        String settingsKey = CmsFormatterConfig.getSettingsKeyForContainer(container.getName());
2196                        String formatterConfigId = element.getSettings() != null
2197                        ? element.getSettings().get(settingsKey)
2198                        : null;
2199                        I_CmsFormatterBean formatterBean = null;
2200                        if (CmsUUID.isValidUUID(formatterConfigId)) {
2201                            formatterBean = OpenCms.getADEManager().getCachedFormatters(
2202                                m_cms.getRequestContext().getCurrentProject().isOnlineProject()).getFormatters().get(
2203                                    new CmsUUID(formatterConfigId));
2204                        }
2205                        if ((formatterBean != null)
2206                            && formatterBean.useMetaMappingsForNormalElements()
2207                            && m_cms.existsResource(element.getId(), filter)) {
2208                            addMappingsForFormatter(formatterBean, element.getId(), resolver, false);
2209                        }
2210
2211                    }
2212                }
2213                if (getDetailContentId() != null) {
2214                    try {
2215                        CmsResource detailContent = m_cms.readResource(
2216                            getDetailContentId(),
2217                            CmsResourceFilter.ignoreExpirationOffline(m_cms));
2218                        CmsFormatterConfiguration config = OpenCms.getADEManager().lookupConfiguration(
2219                            m_cms,
2220                            m_cms.getRequestContext().getRootUri()).getFormatters(m_cms, detailContent);
2221                        for (I_CmsFormatterBean formatter : config.getDetailFormatters()) {
2222                            addMappingsForFormatter(formatter, getDetailContentId(), resolver, true);
2223                        }
2224                    } catch (CmsException e) {
2225                        LOG.error(
2226                            Messages.get().getBundle().key(
2227                                Messages.ERR_READING_REQUIRED_RESOURCE_1,
2228                                getDetailContentId()),
2229                            e);
2230                    }
2231                }
2232            } catch (Exception e) {
2233                LOG.error(e.getLocalizedMessage(), e);
2234            }
2235        }
2236    }
2237
2238    /**
2239     * Initializes the page element data.<p>
2240     */
2241    private void initPageData() {
2242
2243        m_elementInstances = new HashMap<String, CmsContainerElementBean>();
2244        m_parentContainers = new HashMap<String, CmsContainerBean>();
2245        if (m_page != null) {
2246            for (CmsContainerBean container : m_page.getContainers().values()) {
2247                for (CmsContainerElementBean element : container.getElements()) {
2248                    m_elementInstances.put(element.getInstanceId(), element);
2249                    m_parentContainers.put(element.getInstanceId(), container);
2250                    try {
2251                        if (element.isGroupContainer(m_cms) || element.isInheritedContainer(m_cms)) {
2252                            List<CmsContainerElementBean> children;
2253                            if (element.isGroupContainer(m_cms)) {
2254                                children = CmsJspTagContainer.getGroupContainerElements(
2255                                    m_cms,
2256                                    element,
2257                                    m_request,
2258                                    container.getType());
2259                            } else {
2260                                children = CmsJspTagContainer.getInheritedContainerElements(m_cms, element);
2261                            }
2262                            for (CmsContainerElementBean childElement : children) {
2263                                m_elementInstances.put(childElement.getInstanceId(), childElement);
2264                                m_parentContainers.put(childElement.getInstanceId(), container);
2265                            }
2266                        }
2267                    } catch (CmsException e) {
2268                        LOG.error(e.getLocalizedMessage(), e);
2269                    }
2270                }
2271            }
2272            // also add detail only data
2273            if (m_detailOnlyPage != null) {
2274                for (CmsContainerBean container : m_detailOnlyPage.getContainers().values()) {
2275                    for (CmsContainerElementBean element : container.getElements()) {
2276                        m_elementInstances.put(element.getInstanceId(), element);
2277                        m_parentContainers.put(element.getInstanceId(), container);
2278                    }
2279                }
2280            }
2281        }
2282    }
2283
2284}