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