001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp.util;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsADEManager;
032import org.opencms.ade.configuration.CmsFunctionReference;
033import org.opencms.ade.containerpage.CmsContainerpageService;
034import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
035import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
036import org.opencms.ade.detailpage.CmsDetailPageInfo;
037import org.opencms.ade.detailpage.CmsDetailPageResourceHandler;
038import org.opencms.file.CmsFile;
039import org.opencms.file.CmsObject;
040import org.opencms.file.CmsPropertyDefinition;
041import org.opencms.file.CmsRequestContext;
042import org.opencms.file.CmsResource;
043import org.opencms.file.history.CmsHistoryResourceHandler;
044import org.opencms.flex.CmsFlexController;
045import org.opencms.flex.CmsFlexRequest;
046import org.opencms.gwt.shared.CmsGwtConstants;
047import org.opencms.i18n.CmsLocaleGroupService;
048import org.opencms.jsp.CmsJspBean;
049import org.opencms.jsp.CmsJspResourceWrapper;
050import org.opencms.jsp.CmsJspTagContainer;
051import org.opencms.jsp.CmsJspTagEditable;
052import org.opencms.jsp.Messages;
053import org.opencms.jsp.jsonpart.CmsJsonPartFilter;
054import org.opencms.loader.CmsTemplateContextManager;
055import org.opencms.main.CmsException;
056import org.opencms.main.CmsLog;
057import org.opencms.main.CmsRuntimeException;
058import org.opencms.main.CmsSystemInfo;
059import org.opencms.main.OpenCms;
060import org.opencms.relations.CmsCategory;
061import org.opencms.relations.CmsCategoryService;
062import org.opencms.util.CmsCollectionsGenericWrapper;
063import org.opencms.util.CmsStringUtil;
064import org.opencms.util.CmsUUID;
065import org.opencms.xml.containerpage.CmsContainerBean;
066import org.opencms.xml.containerpage.CmsContainerElementBean;
067import org.opencms.xml.containerpage.CmsContainerPageBean;
068import org.opencms.xml.containerpage.CmsDynamicFunctionBean;
069import org.opencms.xml.containerpage.CmsDynamicFunctionParser;
070import org.opencms.xml.containerpage.CmsFormatterConfiguration;
071import org.opencms.xml.containerpage.I_CmsFormatterBean;
072import org.opencms.xml.content.CmsXmlContent;
073import org.opencms.xml.content.CmsXmlContentFactory;
074
075import java.util.ArrayList;
076import java.util.Arrays;
077import java.util.Collections;
078import java.util.HashMap;
079import java.util.List;
080import java.util.Locale;
081import java.util.Map;
082
083import javax.servlet.ServletRequest;
084
085import org.apache.commons.collections.Transformer;
086import org.apache.commons.lang3.LocaleUtils;
087import org.apache.commons.logging.Log;
088
089/**
090 * Allows convenient access to the most important OpenCms functions on a JSP page,
091 * indented to be used from a JSP with the JSTL or EL.<p>
092 *
093 * This bean is available by default in the context of an OpenCms managed JSP.<p>
094 *
095 * @since 8.0
096 */
097public final class CmsJspStandardContextBean {
098
099    /**
100     * Container element wrapper to add some API methods.<p>
101     */
102    public class CmsContainerElementWrapper extends CmsContainerElementBean {
103
104        /** The wrapped element instance. */
105        private CmsContainerElementBean m_wrappedElement;
106
107        /**
108         * Constructor.<p>
109         *
110         * @param element the element to wrap
111         */
112        protected CmsContainerElementWrapper(CmsContainerElementBean element) {
113
114            m_wrappedElement = element;
115
116        }
117
118        /**
119         * @see org.opencms.xml.containerpage.CmsContainerElementBean#clone()
120         */
121        @Override
122        public CmsContainerElementBean clone() {
123
124            return m_wrappedElement.clone();
125        }
126
127        /**
128         * @see org.opencms.xml.containerpage.CmsContainerElementBean#editorHash()
129         */
130        @Override
131        public String editorHash() {
132
133            return m_wrappedElement.editorHash();
134        }
135
136        /**
137         * @see org.opencms.xml.containerpage.CmsContainerElementBean#equals(java.lang.Object)
138         */
139        @Override
140        public boolean equals(Object obj) {
141
142            return m_wrappedElement.equals(obj);
143        }
144
145        /**
146         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getFormatterId()
147         */
148        @Override
149        public CmsUUID getFormatterId() {
150
151            return m_wrappedElement.getFormatterId();
152        }
153
154        /**
155         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getId()
156         */
157        @Override
158        public CmsUUID getId() {
159
160            return m_wrappedElement.getId();
161        }
162
163        /**
164         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getIndividualSettings()
165         */
166        @Override
167        public Map<String, String> getIndividualSettings() {
168
169            return m_wrappedElement.getIndividualSettings();
170        }
171
172        /**
173         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInheritanceInfo()
174         */
175        @Override
176        public CmsInheritanceInfo getInheritanceInfo() {
177
178            return m_wrappedElement.getInheritanceInfo();
179        }
180
181        /**
182         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getInstanceId()
183         */
184        @Override
185        public String getInstanceId() {
186
187            return m_wrappedElement.getInstanceId();
188        }
189
190        /**
191         * Returns the parent element if present.<p>
192         *
193         * @return the parent element or <code>null</code> if not available
194         */
195        public CmsContainerElementWrapper getParent() {
196
197            CmsContainerElementBean parent = getParentElement(m_wrappedElement);
198            return parent != null ? new CmsContainerElementWrapper(getParentElement(m_wrappedElement)) : null;
199        }
200
201        /**
202         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getResource()
203         */
204        @Override
205        public CmsResource getResource() {
206
207            return m_wrappedElement.getResource();
208        }
209
210        /**
211         * Returns the resource type name of the element resource.<p>
212         *
213         * @return the resource type name
214         */
215        public String getResourceTypeName() {
216
217            String result = "";
218            try {
219                result = OpenCms.getResourceManager().getResourceType(m_wrappedElement.getResource()).getTypeName();
220            } catch (Exception e) {
221                CmsJspStandardContextBean.LOG.error(e.getLocalizedMessage(), e);
222            }
223            return result;
224        }
225
226        /**
227         * Returns a lazy initialized setting map.<p>
228         *
229         * @return the settings
230         */
231        public Map<String, ElementSettingWrapper> getSetting() {
232
233            return CmsCollectionsGenericWrapper.createLazyMap(new SettingsTransformer(m_wrappedElement));
234        }
235
236        /**
237         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSettings()
238         */
239        @Override
240        public Map<String, String> getSettings() {
241
242            return m_wrappedElement.getSettings();
243        }
244
245        /**
246         * @see org.opencms.xml.containerpage.CmsContainerElementBean#getSitePath()
247         */
248        @Override
249        public String getSitePath() {
250
251            return m_wrappedElement.getSitePath();
252        }
253
254        /**
255         * @see org.opencms.xml.containerpage.CmsContainerElementBean#hashCode()
256         */
257        @Override
258        public int hashCode() {
259
260            return m_wrappedElement.hashCode();
261        }
262
263        /**
264         * @see org.opencms.xml.containerpage.CmsContainerElementBean#initResource(org.opencms.file.CmsObject)
265         */
266        @Override
267        public void initResource(CmsObject cms) throws CmsException {
268
269            m_wrappedElement.initResource(cms);
270        }
271
272        /**
273         * @see org.opencms.xml.containerpage.CmsContainerElementBean#initSettings(org.opencms.file.CmsObject, org.opencms.xml.containerpage.I_CmsFormatterBean)
274         */
275        @Override
276        public void initSettings(CmsObject cms, I_CmsFormatterBean formatterBean) {
277
278            m_wrappedElement.initSettings(cms, formatterBean);
279        }
280
281        /**
282         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isCreateNew()
283         */
284        @Override
285        public boolean isCreateNew() {
286
287            return m_wrappedElement.isCreateNew();
288        }
289
290        /**
291         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isGroupContainer(org.opencms.file.CmsObject)
292         */
293        @Override
294        public boolean isGroupContainer(CmsObject cms) throws CmsException {
295
296            return m_wrappedElement.isGroupContainer(cms);
297        }
298
299        /**
300         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInheritedContainer(org.opencms.file.CmsObject)
301         */
302        @Override
303        public boolean isInheritedContainer(CmsObject cms) throws CmsException {
304
305            return m_wrappedElement.isInheritedContainer(cms);
306        }
307
308        /**
309         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isInMemoryOnly()
310         */
311        @Override
312        public boolean isInMemoryOnly() {
313
314            return m_wrappedElement.isInMemoryOnly();
315        }
316
317        /**
318         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isReleasedAndNotExpired()
319         */
320        @Override
321        public boolean isReleasedAndNotExpired() {
322
323            return m_wrappedElement.isReleasedAndNotExpired();
324        }
325
326        /**
327         * @see org.opencms.xml.containerpage.CmsContainerElementBean#isTemporaryContent()
328         */
329        @Override
330        public boolean isTemporaryContent() {
331
332            return m_wrappedElement.isTemporaryContent();
333        }
334
335        /**
336         * @see org.opencms.xml.containerpage.CmsContainerElementBean#removeInstanceId()
337         */
338        @Override
339        public void removeInstanceId() {
340
341            m_wrappedElement.removeInstanceId();
342        }
343
344        /**
345         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setFormatterId(org.opencms.util.CmsUUID)
346         */
347        @Override
348        public void setFormatterId(CmsUUID formatterId) {
349
350            m_wrappedElement.setFormatterId(formatterId);
351        }
352
353        /**
354         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setHistoryFile(org.opencms.file.CmsFile)
355         */
356        @Override
357        public void setHistoryFile(CmsFile file) {
358
359            m_wrappedElement.setHistoryFile(file);
360        }
361
362        /**
363         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setInheritanceInfo(org.opencms.ade.containerpage.shared.CmsInheritanceInfo)
364         */
365        @Override
366        public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) {
367
368            m_wrappedElement.setInheritanceInfo(inheritanceInfo);
369        }
370
371        /**
372         * @see org.opencms.xml.containerpage.CmsContainerElementBean#setTemporaryFile(org.opencms.file.CmsFile)
373         */
374        @Override
375        public void setTemporaryFile(CmsFile elementFile) {
376
377            m_wrappedElement.setTemporaryFile(elementFile);
378        }
379
380        /**
381         * @see org.opencms.xml.containerpage.CmsContainerElementBean#toString()
382         */
383        @Override
384        public String toString() {
385
386            return m_wrappedElement.toString();
387        }
388    }
389
390    /**
391     * Provides a lazy initialized Map that provides the detail page link as a value when given the name of a
392     * (named) dynamic function or resource type as a key.<p>
393     */
394    public class CmsDetailLookupTransformer implements Transformer {
395
396        /** The selected prefix. */
397        private String m_prefix;
398
399        /**
400         * Constructor with a prefix.<p>
401         *
402         * The prefix is used to distinguish between type detail pages and function detail pages.<p>
403         *
404         * @param prefix the prefix to use
405         */
406        public CmsDetailLookupTransformer(String prefix) {
407
408            m_prefix = prefix;
409        }
410
411        /**
412         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
413         */
414        @Override
415        public Object transform(Object input) {
416
417            String type = m_prefix + String.valueOf(input);
418            CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
419                m_cms,
420                m_cms.addSiteRoot(m_cms.getRequestContext().getUri()));
421            List<CmsDetailPageInfo> detailPages = config.getDetailPagesForType(type);
422            if ((detailPages == null) || (detailPages.size() == 0)) {
423                return "[No detail page configured for type =" + type + "=]";
424            }
425            CmsDetailPageInfo mainDetailPage = detailPages.get(0);
426            CmsUUID id = mainDetailPage.getId();
427            try {
428                CmsResource r = m_cms.readResource(id);
429                return OpenCms.getLinkManager().substituteLink(m_cms, r);
430            } catch (CmsException e) {
431                LOG.warn(e.getLocalizedMessage(), e);
432                return "[Error reading detail page for type =" + type + "=]";
433            }
434        }
435    }
436
437    /**
438     * Element setting value wrapper.<p>
439     */
440    public class ElementSettingWrapper extends A_CmsJspValueWrapper {
441
442        /** Flag indicating the setting has been configured. */
443        private boolean m_exists;
444
445        /** The wrapped value. */
446        private String m_value;
447
448        /**
449         * Constructor.<p>
450         *
451         * @param value the wrapped value
452         * @param exists flag indicating the setting has been configured
453         */
454        ElementSettingWrapper(String value, boolean exists) {
455
456            m_value = value;
457            m_exists = exists;
458        }
459
460        /**
461         * Returns if the setting has been configured.<p>
462         *
463         * @return <code>true</code> if the setting has been configured
464         */
465        @Override
466        public boolean getExists() {
467
468            return m_exists;
469        }
470
471        /**
472         * Returns if the setting value is null or empty.<p>
473         *
474         * @return <code>true</code> if the setting value is null or empty
475         */
476        @Override
477        public boolean getIsEmpty() {
478
479            return CmsStringUtil.isEmpty(m_value);
480        }
481
482        /**
483         * Returns if the setting value is null or white space only.<p>
484         *
485         * @return <code>true</code> if the setting value is null or white space only
486         */
487        @Override
488        public boolean getIsEmptyOrWhitespaceOnly() {
489
490            return CmsStringUtil.isEmptyOrWhitespaceOnly(m_value);
491        }
492
493        /**
494         * @see org.opencms.jsp.util.A_CmsJspValueWrapper#getIsSet()
495         */
496        @Override
497        public boolean getIsSet() {
498
499            return getExists() && !getIsEmpty();
500        }
501
502        /**
503         * Returns the value.<p>
504         *
505         * @return the value
506         */
507        public String getValue() {
508
509            return m_value;
510        }
511
512        /**
513         * Returns the string value.<p>
514         *
515         * @return the string value
516         */
517        @Override
518        public String toString() {
519
520            return m_value != null ? m_value : "";
521        }
522    }
523
524    /**
525     * The element setting transformer.<p>
526     */
527    public class SettingsTransformer implements Transformer {
528
529        /** The element formatter config. */
530        private I_CmsFormatterBean m_formatter;
531
532        /** The element. */
533        private CmsContainerElementBean m_transformElement;
534
535        /**
536         * Constructor.<p>
537         *
538         * @param element the element
539         */
540        SettingsTransformer(CmsContainerElementBean element) {
541
542            m_transformElement = element;
543            m_formatter = getElementFormatter(element);
544        }
545
546        /**
547         * @see org.apache.commons.collections.Transformer#transform(java.lang.Object)
548         */
549        @Override
550        public Object transform(Object arg0) {
551
552            return new ElementSettingWrapper(
553                m_transformElement.getSettings().get(arg0),
554                m_formatter != null
555                ? m_formatter.getSettings().get(arg0) != null
556                : m_transformElement.getSettings().get(arg0) != null);
557        }
558    }
559
560    /**
561     * Bean containing a template name and URI.<p>
562     */
563    public static class TemplateBean {
564
565        /** True if the template context was manually selected. */
566        private boolean m_forced;
567
568        /** The template name. */
569        private String m_name;
570
571        /** The template resource. */
572        private CmsResource m_resource;
573
574        /** The template uri, if no resource is set. */
575        private String m_uri;
576
577        /**
578         * Creates a new instance.<p>
579         *
580         * @param name the template name
581         * @param resource the template resource
582         */
583        public TemplateBean(String name, CmsResource resource) {
584
585            m_resource = resource;
586            m_name = name;
587        }
588
589        /**
590         * Creates a new instance with an URI instead of a resoure.<p>
591         *
592         * @param name the template name
593         * @param uri the template uri
594         */
595        public TemplateBean(String name, String uri) {
596
597            m_name = name;
598            m_uri = uri;
599        }
600
601        /**
602         * Gets the template name.<p>
603         *
604         * @return the template name
605         */
606        public String getName() {
607
608            return m_name;
609        }
610
611        /**
612         * Gets the template resource.<p>
613         *
614         * @return the template resource
615         */
616        public CmsResource getResource() {
617
618            return m_resource;
619        }
620
621        /**
622         * Gets the template uri.<p>
623         *
624         * @return the template URI.
625         */
626        public String getUri() {
627
628            if (m_resource != null) {
629                return m_resource.getRootPath();
630            } else {
631                return m_uri;
632            }
633        }
634
635        /**
636         * Returns true if the template context was manually selected.<p>
637         *
638         * @return true if the template context was manually selected
639         */
640        public boolean isForced() {
641
642            return m_forced;
643        }
644
645        /**
646         * Sets the 'forced' flag to a new value.<p>
647         *
648         * @param forced the new value
649         */
650        public void setForced(boolean forced) {
651
652            m_forced = forced;
653        }
654
655    }
656
657    /** The attribute name of the cms object.*/
658    public static final String ATTRIBUTE_CMS_OBJECT = "__cmsObject";
659
660    /** The attribute name of the standard JSP context bean. */
661    public static final String ATTRIBUTE_NAME = "cms";
662
663    /** The logger instance for this class. */
664    protected static final Log LOG = CmsLog.getLog(CmsJspStandardContextBean.class);
665
666    /** OpenCms user context. */
667    protected CmsObject m_cms;
668
669    /** Lazily initialized map from a category path to the path's category object. */
670    private Map<String, CmsCategory> m_categories;
671
672    /** Lazily initialized map from a category path to all sub-categories of that category */
673    private Map<String, CmsJspCategoryAccessBean> m_allSubCategories;
674
675    /** The container the currently rendered element is part of. */
676    private CmsContainerBean m_container;
677
678    /** The current detail content resource if available. */
679    private CmsResource m_detailContentResource;
680
681    /** The detail only page references containers that are only displayed in detail view. */
682    private CmsContainerPageBean m_detailOnlyPage;
683
684    /** Flag to indicate if element was just edited. */
685    private boolean m_edited;
686
687    /** The currently rendered element. */
688    private CmsContainerElementBean m_element;
689
690    /** The elements of the current page. */
691    private Map<String, CmsContainerElementBean> m_elementInstances;
692
693    /** The lazy initialized map which allows access to the dynamic function beans. */
694    private Map<String, CmsDynamicFunctionBeanWrapper> m_function;
695
696    /** The lazy initialized map for the function detail pages. */
697    private Map<String, String> m_functionDetailPage;
698
699    /** Indicates if in drag mode. */
700    private boolean m_isDragMode;
701
702    /** Stores the edit mode info. */
703    private Boolean m_isEditMode;
704
705    /** Lazily initialized map from the locale to the localized title property. */
706    private Map<String, String> m_localeTitles;
707
708    /** The currently displayed container page. */
709    private CmsContainerPageBean m_page;
710
711    /** The current container page resource, lazy initialized. */
712    private CmsResource m_pageResource;
713
714    /** The parent containers to the given element instance ids. */
715    private Map<String, CmsContainerBean> m_parentContainers;
716
717    /** Lazily initialized map from a category path to all categories on that path. */
718    private Map<String, List<CmsCategory>> m_pathCategories;
719
720    /** The current request. */
721    private ServletRequest m_request;
722
723    /** Lazily initialized map from the root path of a resource to all categories assigned to the resource. */
724    private Map<String, CmsJspCategoryAccessBean> m_resourceCategories;
725
726    /** The resource wrapper for the current page. */
727    private CmsJspResourceWrapper m_resourceWrapper;
728
729    /** The lazy initialized map for the detail pages. */
730    private Map<String, String> m_typeDetailPage;
731
732    /** The VFS content access bean. */
733    private CmsJspVfsAccessBean m_vfsBean;
734
735    /**
736     * Creates an empty instance.<p>
737     */
738    private CmsJspStandardContextBean() {
739
740        // NOOP
741    }
742
743    /**
744     * Creates a new standard JSP context bean.
745     *
746     * @param req the current servlet request
747     */
748    private CmsJspStandardContextBean(ServletRequest req) {
749
750        CmsFlexController controller = CmsFlexController.getController(req);
751        m_request = req;
752        CmsObject cms;
753        if (controller != null) {
754            cms = controller.getCmsObject();
755        } else {
756            cms = (CmsObject)req.getAttribute(ATTRIBUTE_CMS_OBJECT);
757        }
758        if (cms == null) {
759            // cms object unavailable - this request was not initialized properly
760            throw new CmsRuntimeException(
761                Messages.get().container(Messages.ERR_MISSING_CMS_CONTROLLER_1, CmsJspBean.class.getName()));
762        }
763        updateCmsObject(cms);
764
765        m_detailContentResource = CmsDetailPageResourceHandler.getDetailResource(req);
766    }
767
768    /**
769     * Creates a new instance of the standard JSP context bean.<p>
770     *
771     * To prevent multiple creations of the bean during a request, the OpenCms request context
772     * attributes are used to cache the created VFS access utility bean.<p>
773     *
774     * @param req the current servlet request
775     *
776     * @return a new instance of the standard JSP context bean
777     */
778    public static CmsJspStandardContextBean getInstance(ServletRequest req) {
779
780        Object attribute = req.getAttribute(ATTRIBUTE_NAME);
781        CmsJspStandardContextBean result;
782        if ((attribute != null) && (attribute instanceof CmsJspStandardContextBean)) {
783            result = (CmsJspStandardContextBean)attribute;
784        } else {
785            result = new CmsJspStandardContextBean(req);
786            req.setAttribute(ATTRIBUTE_NAME, result);
787        }
788        return result;
789    }
790
791    /**
792     * Returns a copy of this JSP context bean.<p>
793     *
794     * @return a copy of this JSP context bean
795     */
796    public CmsJspStandardContextBean createCopy() {
797
798        CmsJspStandardContextBean result = new CmsJspStandardContextBean();
799        result.m_container = m_container;
800        if (m_detailContentResource != null) {
801            result.m_detailContentResource = m_detailContentResource.getCopy();
802        }
803        result.m_element = m_element;
804        result.setPage(m_page);
805        return result;
806    }
807
808    /**
809     * Returns a caching hash specific to the element, it's properties and the current container width.<p>
810     *
811     * @return the caching hash
812     */
813    public String elementCachingHash() {
814
815        String result = "";
816        if (m_element != null) {
817            result = m_element.editorHash();
818            if (m_container != null) {
819                result += "w:"
820                    + m_container.getWidth()
821                    + "cName:"
822                    + m_container.getName()
823                    + "cType:"
824                    + m_container.getType();
825            }
826        }
827        return result;
828    }
829
830    /**
831     * Returns the locales available for the currently requested URI.
832     *
833     * @return the locales available for the currently requested URI.
834     */
835    public List<Locale> getAvailableLocales() {
836
837        return OpenCms.getLocaleManager().getAvailableLocales(m_cms, getRequestContext().getUri());
838    }
839
840    /**
841     * Returns the container the currently rendered element is part of.<p>
842     *
843     * @return the currently the currently rendered element is part of
844     */
845    public CmsContainerBean getContainer() {
846
847        return m_container;
848    }
849
850    /**
851     * Returns the current detail content, or <code>null</code> if no detail content is requested.<p>
852     *
853     * @return the current detail content, or <code>null</code> if no detail content is requested.<p>
854     */
855    public CmsResource getDetailContent() {
856
857        return m_detailContentResource;
858    }
859
860    /**
861     * Returns the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p>
862     *
863     * @return the structure id of the current detail content, or <code>null</code> if no detail content is requested.<p>
864     */
865    public CmsUUID getDetailContentId() {
866
867        return m_detailContentResource == null ? null : m_detailContentResource.getStructureId();
868    }
869
870    /**
871     * Returns the detail content site path, or <code>null</code> if not available.<p>
872     *
873     * @return the detail content site path
874     */
875    public String getDetailContentSitePath() {
876
877        return ((m_cms == null) || (m_detailContentResource == null))
878        ? null
879        : m_cms.getSitePath(m_detailContentResource);
880    }
881
882    /**
883     * Returns the detail only page.<p>
884     *
885     * @return the detail only page
886     */
887    public CmsContainerPageBean getDetailOnlyPage() {
888
889        return m_detailOnlyPage;
890    }
891
892    /**
893     * Returns the currently rendered element.<p>
894     *
895     * @return the currently rendered element
896     */
897    public CmsContainerElementWrapper getElement() {
898
899        return m_element != null ? new CmsContainerElementWrapper(m_element) : null;
900    }
901
902    /**
903     * Alternative method name for getReloadMarker().
904     *
905     * @see org.opencms.jsp.util.CmsJspStandardContextBean#getReloadMarker()
906     *
907     * @return the reload marker
908     */
909    public String getEnableReload() {
910
911        return getReloadMarker();
912    }
913
914    /**
915     * Returns a lazy initialized Map which allows access to the dynamic function beans using the JSP EL.<p>
916     *
917     * When given a key, the returned map will look up the corresponding dynamic function bean in the module configuration.<p>
918     *
919     * @return a lazy initialized Map which allows access to the dynamic function beans using the JSP EL
920     */
921    public Map<String, CmsDynamicFunctionBeanWrapper> getFunction() {
922
923        if (m_function == null) {
924
925            Transformer transformer = new Transformer() {
926
927                @Override
928                public Object transform(Object input) {
929
930                    try {
931                        CmsDynamicFunctionBean dynamicFunction = readDynamicFunctionBean((String)input);
932                        CmsDynamicFunctionBeanWrapper wrapper = new CmsDynamicFunctionBeanWrapper(
933                            m_cms,
934                            dynamicFunction);
935                        return wrapper;
936
937                    } catch (CmsException e) {
938                        LOG.debug(e.getLocalizedMessage(), e);
939                        return new CmsDynamicFunctionBeanWrapper(m_cms, null);
940                    }
941                }
942            };
943            m_function = CmsCollectionsGenericWrapper.createLazyMap(transformer);
944        }
945        return m_function;
946
947    }
948
949    /**
950     * Deprecated method to access function detail pages using the EL.<p>
951     *
952     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
953     * (named) dynamic function as a key
954     *
955     * @deprecated use {@link #getFunctionDetailPage()} instead
956     */
957    @Deprecated
958    public Map<String, String> getFunctionDetail() {
959
960        return getFunctionDetailPage();
961    }
962
963    /**
964     * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a
965     * (named) dynamic function as a key.<p>
966     *
967     * The provided Map key is assumed to be a String that represents a named dynamic function.<p>
968     *
969     * Usage example on a JSP with the JSTL:<pre>
970     * &lt;a href=${cms.functionDetailPage['search']} /&gt
971     * </pre>
972     *
973     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
974     * (named) dynamic function as a key
975     *
976     * @see #getTypeDetailPage()
977     */
978    public Map<String, String> getFunctionDetailPage() {
979
980        if (m_functionDetailPage == null) {
981            m_functionDetailPage = CmsCollectionsGenericWrapper.createLazyMap(
982                new CmsDetailLookupTransformer(CmsDetailPageInfo.FUNCTION_PREFIX));
983        }
984        return m_functionDetailPage;
985    }
986
987    /**
988     * Returns a lazy map which creates a wrapper object for a dynamic function format when given an XML content
989     * as a key.<p>
990     *
991     * @return a lazy map for accessing function formats for a content
992     */
993    public Map<CmsJspContentAccessBean, CmsDynamicFunctionFormatWrapper> getFunctionFormatFromContent() {
994
995        Transformer transformer = new Transformer() {
996
997            @Override
998            public Object transform(Object contentAccess) {
999
1000                CmsXmlContent content = (CmsXmlContent)(((CmsJspContentAccessBean)contentAccess).getRawContent());
1001                CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser();
1002                CmsDynamicFunctionBean functionBean = null;
1003                try {
1004                    functionBean = parser.parseFunctionBean(m_cms, content);
1005                } catch (CmsException e) {
1006                    LOG.debug(e.getLocalizedMessage(), e);
1007                    return new CmsDynamicFunctionFormatWrapper(m_cms, null);
1008                }
1009                String type = getContainer().getType();
1010                String width = getContainer().getWidth();
1011                int widthNum = -1;
1012                try {
1013                    widthNum = Integer.parseInt(width);
1014                } catch (NumberFormatException e) {
1015                    LOG.debug(e.getLocalizedMessage(), e);
1016                }
1017                CmsDynamicFunctionBean.Format format = functionBean.getFormatForContainer(m_cms, type, widthNum);
1018                CmsDynamicFunctionFormatWrapper wrapper = new CmsDynamicFunctionFormatWrapper(m_cms, format);
1019                return wrapper;
1020            }
1021        };
1022        return CmsCollectionsGenericWrapper.createLazyMap(transformer);
1023    }
1024
1025    /**
1026     * Checks if the current request should be direct edit enabled.
1027     * Online-, history-requests, previews and temporary files will not be editable.<p>
1028     *
1029     * @return <code>true</code> if the current request should be direct edit enabled
1030     */
1031    public boolean getIsEditMode() {
1032
1033        if (m_isEditMode == null) {
1034            m_isEditMode = Boolean.valueOf(CmsJspTagEditable.isEditableRequest(m_request));
1035        }
1036        return m_isEditMode.booleanValue();
1037    }
1038
1039    /**
1040     * Returns true if the current request is a JSON request.<p>
1041     *
1042     * @return true if we are in a JSON request
1043     */
1044    public boolean getIsJSONRequest() {
1045
1046        return CmsJsonPartFilter.isJsonRequest(m_request);
1047    }
1048
1049    /**
1050     * Returns if the current project is the online project.<p>
1051     *
1052     * @return <code>true</code> if the current project is the online project
1053     */
1054    public boolean getIsOnlineProject() {
1055
1056        return m_cms.getRequestContext().getCurrentProject().isOnlineProject();
1057    }
1058
1059    /**
1060     * Returns the current locale.<p>
1061     *
1062     * @return the current locale
1063     */
1064    public Locale getLocale() {
1065
1066        return getRequestContext().getLocale();
1067    }
1068
1069    /**
1070     * Gets a map providing access to the locale variants of the current page.<p>
1071     *
1072     * Note that all available locales for the site / subsite are used as keys, not just the ones for which a locale
1073     * variant actually exists.
1074     *
1075     * Usage in JSPs: ${cms.localeResource['de']]
1076     *
1077     * @return the map from locale strings to locale variant resources
1078     */
1079    public Map<String, CmsJspResourceWrapper> getLocaleResource() {
1080
1081        Map<String, CmsJspResourceWrapper> result = getResourceWrapperForPage().getLocaleResource();
1082        List<Locale> locales = CmsLocaleGroupService.getPossibleLocales(m_cms, getContainerPage());
1083        for (Locale locale : locales) {
1084            if (!result.containsKey(locale.toString())) {
1085                result.put(locale.toString(), null);
1086            }
1087        }
1088        return result;
1089    }
1090
1091    /**
1092     * Gets the main locale for the current page's locale group.<p>
1093     *
1094     * @return the main locale for the current page's locale group
1095     */
1096    public Locale getMainLocale() {
1097
1098        return getResourceWrapperForPage().getMainLocale();
1099    }
1100
1101    /**
1102     * Returns the currently displayed container page.<p>
1103     *
1104     * @return the currently displayed container page
1105     */
1106    public CmsContainerPageBean getPage() {
1107
1108        return m_page;
1109    }
1110
1111    /**
1112     * Returns the parent container to the current container if available.<p>
1113     *
1114     * @return the parent container
1115     */
1116    public CmsContainerBean getParentContainer() {
1117
1118        CmsContainerBean result = null;
1119        if ((getContainer() != null) && (getContainer().getParentInstanceId() != null)) {
1120            result = m_parentContainers.get(getContainer().getParentInstanceId());
1121        }
1122        return result;
1123    }
1124
1125    /**
1126     * Returns the instance id parent container mapping.<p>
1127     *
1128     * @return the instance id parent container mapping
1129     */
1130    public Map<String, CmsContainerBean> getParentContainers() {
1131
1132        if (m_parentContainers == null) {
1133            initPageData();
1134        }
1135        return Collections.unmodifiableMap(m_parentContainers);
1136    }
1137
1138    /**
1139     * Returns the parent element to the current element if available.<p>
1140     *
1141     * @return the parent element or null
1142     */
1143    public CmsContainerElementBean getParentElement() {
1144
1145        return getParentElement(getElement());
1146    }
1147
1148    /**
1149     * JSP EL accessor method for retrieving the preview formatters.<p>
1150     *
1151     * @return a lazy map for accessing preview formatters
1152     */
1153    public Map<String, String> getPreviewFormatter() {
1154
1155        Transformer transformer = new Transformer() {
1156
1157            @Override
1158            public Object transform(Object uri) {
1159
1160                try {
1161                    String rootPath = m_cms.getRequestContext().addSiteRoot((String)uri);
1162                    CmsResource resource = m_cms.readResource((String)uri);
1163                    CmsADEManager adeManager = OpenCms.getADEManager();
1164                    CmsADEConfigData configData = adeManager.lookupConfiguration(m_cms, rootPath);
1165                    CmsFormatterConfiguration formatterConfig = configData.getFormatters(m_cms, resource);
1166                    if (formatterConfig == null) {
1167                        return "";
1168                    }
1169                    I_CmsFormatterBean previewFormatter = formatterConfig.getPreviewFormatter();
1170                    if (previewFormatter == null) {
1171                        return "";
1172                    }
1173                    CmsUUID structureId = previewFormatter.getJspStructureId();
1174                    m_cms.readResource(structureId);
1175                    CmsResource formatterResource = m_cms.readResource(structureId);
1176                    String formatterSitePath = m_cms.getRequestContext().removeSiteRoot(
1177                        formatterResource.getRootPath());
1178                    return formatterSitePath;
1179                } catch (CmsException e) {
1180                    LOG.warn(e.getLocalizedMessage(), e);
1181                    return "";
1182                }
1183            }
1184        };
1185        return CmsCollectionsGenericWrapper.createLazyMap(transformer);
1186    }
1187
1188    /**
1189     * Reads all sub-categories below the provided category.
1190     * @return The map from the provided category to it's sub-categories in a {@link CmsJspCategoryAccessBean}.
1191     */
1192    public Map<String, CmsJspCategoryAccessBean> getReadAllSubCategories() {
1193
1194        if (null == m_allSubCategories) {
1195            m_allSubCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1196
1197                @Override
1198                public Object transform(Object categoryPath) {
1199
1200                    try {
1201                        List<CmsCategory> categories = CmsCategoryService.getInstance().readCategories(
1202                            m_cms,
1203                            (String)categoryPath,
1204                            true,
1205                            m_cms.getRequestContext().getUri());
1206                        CmsJspCategoryAccessBean result = new CmsJspCategoryAccessBean(
1207                            categories,
1208                            (String)categoryPath);
1209                        return result;
1210                    } catch (CmsException e) {
1211                        LOG.warn(e.getLocalizedMessage(), e);
1212                        return null;
1213                    }
1214                }
1215
1216            });
1217        }
1218        return m_allSubCategories;
1219    }
1220
1221    /**
1222     * Reads the categories assigned to the currently requested URI.
1223     * @return the categories assigned to the currently requested URI.
1224     */
1225    public CmsJspCategoryAccessBean getReadCategories() {
1226
1227        return m_resourceCategories.get(getRequestContext().getUri());
1228    }
1229
1230    /**
1231     * Transforms the category path of a category to the category.
1232     * @return a map from root or site path to category.
1233     */
1234    public Map<String, CmsCategory> getReadCategory() {
1235
1236        if (null == m_categories) {
1237            m_categories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1238
1239                public Object transform(Object categoryPath) {
1240
1241                    try {
1242                        return CmsCategoryService.getInstance().readCategory(
1243                            m_cms,
1244                            (String)categoryPath,
1245                            getRequestContext().getUri());
1246                    } catch (CmsException e) {
1247                        LOG.warn(e.getLocalizedMessage(), e);
1248                        return null;
1249                    }
1250                }
1251
1252            });
1253        }
1254        return m_categories;
1255    }
1256
1257    /**
1258     * Transforms the category path to the list of all categories on that path.<p>
1259     *
1260     * Example: For path <code>"location/europe/"</code>
1261     *          the list <code>[getReadCategory.get("location/"),getReadCategory.get("location/europe/")]</code>
1262     *          is returned.
1263     * @return a map from a category path to list of categories on that path.
1264     */
1265    public Map<String, List<CmsCategory>> getReadPathCategories() {
1266
1267        if (null == m_pathCategories) {
1268            m_pathCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1269
1270                public Object transform(Object categoryPath) {
1271
1272                    List<CmsCategory> result = new ArrayList<CmsCategory>();
1273
1274                    String path = (String)categoryPath;
1275
1276                    if ((null == path) || (path.length() <= 1)) {
1277                        return result;
1278                    }
1279
1280                    //cut last slash
1281                    path = path.substring(0, path.length() - 1);
1282
1283                    List<String> pathParts = Arrays.asList(path.split("/"));
1284
1285                    String currentPath = "";
1286                    for (String part : pathParts) {
1287                        currentPath += part + "/";
1288                        CmsCategory category = getReadCategory().get(currentPath);
1289                        if (null != category) {
1290                            result.add(category);
1291                        }
1292                    }
1293                    return result;
1294                }
1295
1296            });
1297        }
1298        return m_pathCategories;
1299    }
1300
1301    /**
1302     * Reads the categories assigned to a resource.
1303     *
1304     * @return map from the resource path (root path) to the assigned categories
1305     */
1306    public Map<String, CmsJspCategoryAccessBean> getReadResourceCategories() {
1307
1308        if (null == m_resourceCategories) {
1309            m_resourceCategories = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1310
1311                public Object transform(Object resourceName) {
1312
1313                    try {
1314                        CmsResource resource = m_cms.readResource(
1315                            getRequestContext().removeSiteRoot((String)resourceName));
1316                        return new CmsJspCategoryAccessBean(m_cms, resource);
1317                    } catch (CmsException e) {
1318                        LOG.warn(e.getLocalizedMessage(), e);
1319                        return null;
1320                    }
1321                }
1322            });
1323        }
1324        return m_resourceCategories;
1325    }
1326
1327    /**
1328     * Returns a HTML comment string that will cause the container page editor to reload the page if the element or its settings
1329     * were edited.<p>
1330     *
1331     * @return the reload marker
1332     */
1333    public String getReloadMarker() {
1334
1335        if (m_cms.getRequestContext().getCurrentProject().isOnlineProject()) {
1336            return ""; // reload marker is not needed in Online mode
1337        } else {
1338            return CmsGwtConstants.FORMATTER_RELOAD_MARKER;
1339        }
1340    }
1341
1342    /**
1343     * Returns the request context.<p>
1344     *
1345     * @return the request context
1346     */
1347    public CmsRequestContext getRequestContext() {
1348
1349        return m_cms.getRequestContext();
1350    }
1351
1352    /**
1353     * Returns the subsite path for the currently requested URI.<p>
1354     *
1355     * @return the subsite path
1356     */
1357    public String getSubSitePath() {
1358
1359        return m_cms.getRequestContext().removeSiteRoot(
1360            OpenCms.getADEManager().getSubSiteRoot(m_cms, m_cms.getRequestContext().getRootUri()));
1361    }
1362
1363    /**
1364     * Returns the system information.<p>
1365     *
1366     * @return the system information
1367     */
1368    public CmsSystemInfo getSystemInfo() {
1369
1370        return OpenCms.getSystemInfo();
1371    }
1372
1373    /**
1374     * Gets a bean containing information about the current template.<p>
1375     *
1376     * @return the template information bean
1377     */
1378    public TemplateBean getTemplate() {
1379
1380        TemplateBean templateBean = getRequestAttribute(CmsTemplateContextManager.ATTR_TEMPLATE_BEAN);
1381        if (templateBean == null) {
1382            templateBean = new TemplateBean("", "");
1383        }
1384        return templateBean;
1385    }
1386
1387    /**
1388     * Returns the title of a page delivered from OpenCms, usually used for the <code>&lt;title&gt;</code> tag of
1389     * a HTML page.<p>
1390     *
1391     * If no title information has been found, the empty String "" is returned.<p>
1392     *
1393     * @return the title of the current page
1394     */
1395    public String getTitle() {
1396
1397        return getLocaleSpecificTitle(null);
1398
1399    }
1400
1401    /**
1402     * Get the title and read the Title property according the provided locale.
1403     * @return The map from locales to the locale specific titles.
1404     */
1405    public Map<String, String> getTitleLocale() {
1406
1407        if (m_localeTitles == null) {
1408            m_localeTitles = CmsCollectionsGenericWrapper.createLazyMap(new Transformer() {
1409
1410                public Object transform(Object inputLocale) {
1411
1412                    Locale locale = null;
1413                    if (null != inputLocale) {
1414                        if (inputLocale instanceof Locale) {
1415                            locale = (Locale)inputLocale;
1416                        } else if (inputLocale instanceof String) {
1417                            try {
1418                                locale = LocaleUtils.toLocale((String)inputLocale);
1419                            } catch (IllegalArgumentException | NullPointerException e) {
1420                                // do nothing, just go on without locale
1421                            }
1422                        }
1423                    }
1424                    return getLocaleSpecificTitle(locale);
1425                }
1426
1427            });
1428        }
1429        return m_localeTitles;
1430    }
1431
1432    /**
1433     * Returns a lazy initialized Map that provides the detail page link as a value when given the name of a
1434     * resource type as a key.<p>
1435     *
1436     * The provided Map key is assumed to be the name of a resource type that has a detail page configured.<p>
1437     *
1438     * Usage example on a JSP with the JSTL:<pre>
1439     * &lt;a href=${cms.typeDetailPage['bs-blog']} /&gt
1440     * </pre>
1441     *
1442     * @return a lazy initialized Map that provides the detail page link as a value when given the name of a
1443     * resource type as a key
1444     *
1445     * @see #getFunctionDetailPage()
1446     */
1447    public Map<String, String> getTypeDetailPage() {
1448
1449        if (m_typeDetailPage == null) {
1450            m_typeDetailPage = CmsCollectionsGenericWrapper.createLazyMap(new CmsDetailLookupTransformer(""));
1451        }
1452        return m_typeDetailPage;
1453    }
1454
1455    /**
1456     * Returns an initialized VFS access bean.<p>
1457     *
1458     * @return an initialized VFS access bean
1459     */
1460    public CmsJspVfsAccessBean getVfs() {
1461
1462        if (m_vfsBean == null) {
1463            // create a new VVFS access bean
1464            m_vfsBean = CmsJspVfsAccessBean.create(m_cms);
1465        }
1466        return m_vfsBean;
1467    }
1468
1469    /**
1470     * Returns the workplace locale from the current user's settings.<p>
1471     *
1472     * @return returns the workplace locale from the current user's settings
1473     */
1474    public Locale getWorkplaceLocale() {
1475
1476        return OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
1477    }
1478
1479    /**
1480     * Returns <code>true</code in case a detail page is available for the current element.<p>
1481     *
1482     * @return <code>true</code in case a detail page is available for the current element
1483     */
1484    public boolean isDetailPageAvailable() {
1485
1486        boolean result = false;
1487        if ((m_cms != null)
1488            && (m_element != null)
1489            && !m_element.isInMemoryOnly()
1490            && (m_element.getResource() != null)) {
1491            try {
1492                String detailPage = OpenCms.getADEManager().getDetailPageFinder().getDetailPage(
1493                    m_cms,
1494                    m_element.getResource().getRootPath(),
1495                    m_cms.getRequestContext().getUri(),
1496                    null);
1497                result = detailPage != null;
1498            } catch (CmsException e) {
1499                LOG.warn(e.getLocalizedMessage(), e);
1500            }
1501        }
1502        return result;
1503    }
1504
1505    /**
1506     * Returns <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise.<p>
1507     *
1508     * Same as to check if {@link #getDetailContent()} is <code>null</code>.<p>
1509     *
1510     * @return <code>true</code> if this is a request to a detail resource, <code>false</code> otherwise
1511     */
1512    public boolean isDetailRequest() {
1513
1514        return m_detailContentResource != null;
1515    }
1516
1517    /**
1518     * Returns if the page is in drag mode.<p>
1519     *
1520     * @return if the page is in drag mode
1521     */
1522    public boolean isDragMode() {
1523
1524        return m_isDragMode;
1525    }
1526
1527    /**
1528     * Returns the flag to indicate if in drag and drop mode.<p>
1529     *
1530     * @return <code>true</code> if in drag and drop mode
1531     */
1532    public boolean isEdited() {
1533
1534        return m_edited;
1535    }
1536
1537    /**
1538     * Returns if the current element is a model group.<p>
1539     *
1540     * @return <code>true</code> if the current element is a model group
1541     */
1542    public boolean isModelGroupElement() {
1543
1544        return (m_element != null) && !m_element.isInMemoryOnly() && isModelGroupPage() && m_element.isModelGroup();
1545    }
1546
1547    /**
1548     * Returns if the current page is used to manage model groups.<p>
1549     *
1550     * @return <code>true</code> if the current page is used to manage model groups
1551     */
1552    public boolean isModelGroupPage() {
1553
1554        CmsResource page = getContainerPage();
1555        return (page != null) && CmsContainerpageService.isEditingModelGroups(m_cms, page);
1556
1557    }
1558
1559    /**
1560     * Sets the container the currently rendered element is part of.<p>
1561     *
1562     * @param container the container the currently rendered element is part of
1563     */
1564    public void setContainer(CmsContainerBean container) {
1565
1566        m_container = container;
1567    }
1568
1569    /**
1570     * Sets the detail only page.<p>
1571     *
1572     * @param detailOnlyPage the detail only page to set
1573     */
1574    public void setDetailOnlyPage(CmsContainerPageBean detailOnlyPage) {
1575
1576        m_detailOnlyPage = detailOnlyPage;
1577        clearPageData();
1578    }
1579
1580    /**
1581     * Sets if the page is in drag mode.<p>
1582     *
1583     * @param isDragMode if the page is in drag mode
1584     */
1585    public void setDragMode(boolean isDragMode) {
1586
1587        m_isDragMode = isDragMode;
1588    }
1589
1590    /**
1591     * Sets the flag to indicate if in drag and drop mode.<p>
1592     *
1593     * @param edited <code>true</code> if in drag and drop mode
1594     */
1595    public void setEdited(boolean edited) {
1596
1597        m_edited = edited;
1598    }
1599
1600    /**
1601     * Sets the currently rendered element.<p>
1602     *
1603     * @param element the currently rendered element to set
1604     */
1605    public void setElement(CmsContainerElementBean element) {
1606
1607        m_element = element;
1608    }
1609
1610    /**
1611     * Sets the currently displayed container page.<p>
1612     *
1613     * @param page the currently displayed container page to set
1614     */
1615    public void setPage(CmsContainerPageBean page) {
1616
1617        m_page = page;
1618        clearPageData();
1619    }
1620
1621    /**
1622     * Updates the internally stored OpenCms user context.<p>
1623     *
1624     * @param cms the new OpenCms user context
1625     */
1626    public void updateCmsObject(CmsObject cms) {
1627
1628        try {
1629            m_cms = OpenCms.initCmsObject(cms);
1630        } catch (CmsException e) {
1631            LOG.error(e.getLocalizedMessage(), e);
1632            m_cms = cms;
1633        }
1634    }
1635
1636    /**
1637     * Updates the standard context bean from the request.<p>
1638     *
1639     * @param cmsFlexRequest the request from which to update the data
1640     */
1641    public void updateRequestData(CmsFlexRequest cmsFlexRequest) {
1642
1643        CmsResource detailRes = CmsDetailPageResourceHandler.getDetailResource(cmsFlexRequest);
1644        m_detailContentResource = detailRes;
1645        m_request = cmsFlexRequest;
1646
1647    }
1648
1649    /**
1650     * Returns the formatter configuration to the given element.<p>
1651     *
1652     * @param element the element
1653     *
1654     * @return the formatter configuration
1655     */
1656    protected I_CmsFormatterBean getElementFormatter(CmsContainerElementBean element) {
1657
1658        if (m_elementInstances == null) {
1659            initPageData();
1660        }
1661        I_CmsFormatterBean formatter = null;
1662        CmsContainerBean container = m_parentContainers.get(element.getInstanceId());
1663        if (container == null) {
1664            // use the current container
1665            container = getContainer();
1666        }
1667        String containerName = container.getName();
1668        Map<String, String> settings = element.getSettings();
1669        if (settings != null) {
1670            String formatterConfigId = settings.get(CmsFormatterConfig.getSettingsKeyForContainer(containerName));
1671            if (CmsUUID.isValidUUID(formatterConfigId)) {
1672                formatter = OpenCms.getADEManager().getCachedFormatters(false).getFormatters().get(
1673                    new CmsUUID(formatterConfigId));
1674            }
1675        }
1676        if (formatter == null) {
1677            try {
1678                CmsResource resource = m_cms.readResource(m_cms.getRequestContext().getUri());
1679
1680                CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(m_cms, resource.getRootPath());
1681                CmsFormatterConfiguration formatters = config.getFormatters(m_cms, resource);
1682                int width = -2;
1683                try {
1684                    width = Integer.parseInt(container.getWidth());
1685                } catch (Exception e) {
1686                    LOG.debug(e.getLocalizedMessage(), e);
1687                }
1688                formatter = formatters.getDefaultSchemaFormatter(container.getType(), width);
1689            } catch (CmsException e1) {
1690                LOG.error(e1.getLocalizedMessage(), e1);
1691            }
1692        }
1693        return formatter;
1694    }
1695
1696    /**
1697     * Returns the title according to the given locale.
1698     * @param locale the locale for which the title should be read.
1699     * @return the title according to the given locale
1700     */
1701    protected String getLocaleSpecificTitle(Locale locale) {
1702
1703        String result = null;
1704
1705        try {
1706
1707            if (isDetailRequest()) {
1708                // this is a request to a detail page
1709                CmsResource res = getDetailContent();
1710                CmsFile file = m_cms.readFile(res);
1711                CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, file);
1712                result = content.getHandler().getTitleMapping(m_cms, content, m_cms.getRequestContext().getLocale());
1713                if (result == null) {
1714                    // title not found, maybe no mapping OR not available in the current locale
1715                    // read the title of the detail resource as fall back (may contain mapping from another locale)
1716                    result = m_cms.readPropertyObject(
1717                        res,
1718                        CmsPropertyDefinition.PROPERTY_TITLE,
1719                        false,
1720                        locale).getValue();
1721                }
1722            }
1723            if (result == null) {
1724                // read the title of the requested resource as fall back
1725                result = m_cms.readPropertyObject(
1726                    m_cms.getRequestContext().getUri(),
1727                    CmsPropertyDefinition.PROPERTY_TITLE,
1728                    true,
1729                    locale).getValue();
1730            }
1731        } catch (CmsException e) {
1732            LOG.debug(e.getLocalizedMessage(), e);
1733        }
1734        if (CmsStringUtil.isEmptyOrWhitespaceOnly(result)) {
1735            result = "";
1736        }
1737
1738        return result;
1739    }
1740
1741    /**
1742     * Returns the parent element if available.<p>
1743     *
1744     * @param element the element
1745     *
1746     * @return the parent element or null
1747     */
1748    protected CmsContainerElementBean getParentElement(CmsContainerElementBean element) {
1749
1750        if (m_elementInstances == null) {
1751            initPageData();
1752        }
1753        CmsContainerElementBean parent = null;
1754        CmsContainerBean cont = m_parentContainers.get(element.getInstanceId());
1755        if ((cont != null) && cont.isNestedContainer()) {
1756            parent = m_elementInstances.get(cont.getParentInstanceId());
1757        }
1758        return parent;
1759    }
1760
1761    /**
1762     * Reads a dynamic function bean, given its name in the module configuration.<p>
1763     *
1764     * @param configuredName the name of the dynamic function in the module configuration
1765     * @return the dynamic function bean for the dynamic function configured under that name
1766     *
1767     * @throws CmsException if something goes wrong
1768     */
1769    protected CmsDynamicFunctionBean readDynamicFunctionBean(String configuredName) throws CmsException {
1770
1771        CmsADEConfigData config = OpenCms.getADEManager().lookupConfiguration(
1772            m_cms,
1773            m_cms.addSiteRoot(m_cms.getRequestContext().getUri()));
1774        CmsFunctionReference functionRef = config.getFunctionReference(configuredName);
1775        if (functionRef == null) {
1776            return null;
1777        }
1778        CmsDynamicFunctionParser parser = new CmsDynamicFunctionParser();
1779        CmsResource functionResource = m_cms.readResource(functionRef.getStructureId());
1780        CmsDynamicFunctionBean result = parser.parseFunctionBean(m_cms, functionResource);
1781        return result;
1782    }
1783
1784    /**
1785     * Clears the page element data.<p>
1786     */
1787    private void clearPageData() {
1788
1789        m_elementInstances = null;
1790        m_parentContainers = null;
1791    }
1792
1793    /**
1794     * Returns the current container page resource.<p>
1795     *
1796     * @return the current container page resource
1797     */
1798    private CmsResource getContainerPage() {
1799
1800        try {
1801            if (m_pageResource == null) {
1802                // get the container page itself, checking the history first
1803                m_pageResource = (CmsResource)CmsHistoryResourceHandler.getHistoryResource(m_request);
1804                if (m_pageResource == null) {
1805                    m_pageResource = m_cms.readResource(m_cms.getRequestContext().getUri());
1806                }
1807            }
1808        } catch (CmsException e) {
1809            LOG.error(e.getLocalizedMessage(), e);
1810        }
1811        return m_pageResource;
1812    }
1813
1814    /**
1815     * Convenience method for getting a request attribute without an explicit cast.<p>
1816     *
1817     * @param name the attribute name
1818     * @return the request attribute
1819     */
1820    @SuppressWarnings("unchecked")
1821    private <A> A getRequestAttribute(String name) {
1822
1823        Object attribute = m_request.getAttribute(name);
1824
1825        return attribute != null ? (A)attribute : null;
1826    }
1827
1828    /**
1829     * Gets the resource wrapper for the current page, initializing it if necessary.<p>
1830     *
1831     * @return the resource wrapper for the current page
1832     */
1833    private CmsJspResourceWrapper getResourceWrapperForPage() {
1834
1835        if (m_resourceWrapper != null) {
1836            return m_resourceWrapper;
1837        }
1838        m_resourceWrapper = new CmsJspResourceWrapper(m_cms, getContainerPage());
1839        return m_resourceWrapper;
1840    }
1841
1842    /**
1843     * Initializes the page element data.<p>
1844     */
1845    private void initPageData() {
1846
1847        m_elementInstances = new HashMap<String, CmsContainerElementBean>();
1848        m_parentContainers = new HashMap<String, CmsContainerBean>();
1849        if (m_page != null) {
1850            for (CmsContainerBean container : m_page.getContainers().values()) {
1851                for (CmsContainerElementBean element : container.getElements()) {
1852                    m_elementInstances.put(element.getInstanceId(), element);
1853                    m_parentContainers.put(element.getInstanceId(), container);
1854                    try {
1855                        if (element.isGroupContainer(m_cms) || element.isInheritedContainer(m_cms)) {
1856                            List<CmsContainerElementBean> children;
1857                            if (element.isGroupContainer(m_cms)) {
1858                                children = CmsJspTagContainer.getGroupContainerElements(
1859                                    m_cms,
1860                                    element,
1861                                    m_request,
1862                                    container.getType());
1863                            } else {
1864                                children = CmsJspTagContainer.getInheritedContainerElements(m_cms, element);
1865                            }
1866                            for (CmsContainerElementBean childElement : children) {
1867                                m_elementInstances.put(childElement.getInstanceId(), childElement);
1868                                m_parentContainers.put(childElement.getInstanceId(), container);
1869                            }
1870                        }
1871                    } catch (CmsException e) {
1872                        LOG.error(e.getLocalizedMessage(), e);
1873                    }
1874                }
1875            }
1876            // also add detail only data
1877            if (m_detailOnlyPage != null) {
1878                for (CmsContainerBean container : m_detailOnlyPage.getContainers().values()) {
1879                    for (CmsContainerElementBean element : container.getElements()) {
1880                        m_elementInstances.put(element.getInstanceId(), element);
1881                        m_parentContainers.put(element.getInstanceId(), container);
1882                    }
1883                }
1884            }
1885        }
1886    }
1887
1888}