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.ade.containerpage;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsElementView;
032import org.opencms.ade.configuration.CmsResourceTypeConfig;
033import org.opencms.ade.containerpage.inherited.CmsContainerConfigurationCache;
034import org.opencms.ade.containerpage.inherited.CmsInheritanceReference;
035import org.opencms.ade.containerpage.inherited.CmsInheritanceReferenceParser;
036import org.opencms.ade.containerpage.inherited.CmsInheritedContainerState;
037import org.opencms.ade.containerpage.shared.CmsContainer;
038import org.opencms.ade.containerpage.shared.CmsContainerElement;
039import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState;
040import org.opencms.ade.containerpage.shared.CmsContainerElementData;
041import org.opencms.ade.containerpage.shared.CmsElementLockInfo;
042import org.opencms.ade.containerpage.shared.CmsElementSettingsConfig;
043import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
044import org.opencms.ade.containerpage.shared.CmsFormatterConfigCollection;
045import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
046import org.opencms.ade.detailpage.CmsDetailPageResourceHandler;
047import org.opencms.configuration.CmsConfigurationException;
048import org.opencms.db.CmsResourceState;
049import org.opencms.file.CmsFile;
050import org.opencms.file.CmsObject;
051import org.opencms.file.CmsRequestContext;
052import org.opencms.file.CmsResource;
053import org.opencms.file.CmsResourceFilter;
054import org.opencms.file.CmsUser;
055import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
056import org.opencms.file.types.CmsResourceTypeXmlContent;
057import org.opencms.file.types.I_CmsResourceType;
058import org.opencms.gwt.CmsDefaultResourceStatusProvider;
059import org.opencms.gwt.CmsIconUtil;
060import org.opencms.gwt.CmsVfsService;
061import org.opencms.gwt.shared.CmsAdditionalInfoBean;
062import org.opencms.gwt.shared.CmsListInfoBean;
063import org.opencms.gwt.shared.CmsPermissionInfo;
064import org.opencms.i18n.CmsMessageContainer;
065import org.opencms.i18n.CmsMessages;
066import org.opencms.jsp.util.CmsJspStandardContextBean;
067import org.opencms.jsp.util.CmsJspStandardContextBean.TemplateBean;
068import org.opencms.loader.CmsTemplateContextManager;
069import org.opencms.lock.CmsLock;
070import org.opencms.main.CmsException;
071import org.opencms.main.CmsLog;
072import org.opencms.main.OpenCms;
073import org.opencms.relations.CmsRelation;
074import org.opencms.relations.CmsRelationFilter;
075import org.opencms.relations.CmsRelationType;
076import org.opencms.search.galleries.CmsGallerySearch;
077import org.opencms.search.galleries.CmsGallerySearchResult;
078import org.opencms.util.CmsDateUtil;
079import org.opencms.util.CmsStringUtil;
080import org.opencms.util.CmsUUID;
081import org.opencms.workplace.CmsWorkplaceMessages;
082import org.opencms.workplace.editors.CmsWorkplaceEditorManager;
083import org.opencms.workplace.editors.directedit.CmsAdvancedDirectEditProvider;
084import org.opencms.workplace.editors.directedit.CmsDirectEditMode;
085import org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider;
086import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
087import org.opencms.workplace.explorer.CmsResourceUtil;
088import org.opencms.xml.containerpage.CmsADESessionCache;
089import org.opencms.xml.containerpage.CmsContainerBean;
090import org.opencms.xml.containerpage.CmsContainerElementBean;
091import org.opencms.xml.containerpage.CmsContainerPageBean;
092import org.opencms.xml.containerpage.CmsFormatterConfiguration;
093import org.opencms.xml.containerpage.CmsGroupContainerBean;
094import org.opencms.xml.containerpage.CmsXmlContainerPage;
095import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
096import org.opencms.xml.containerpage.CmsXmlGroupContainer;
097import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory;
098import org.opencms.xml.containerpage.I_CmsFormatterBean;
099import org.opencms.xml.content.CmsXmlContent;
100import org.opencms.xml.content.CmsXmlContentFactory;
101import org.opencms.xml.content.CmsXmlContentProperty;
102import org.opencms.xml.content.CmsXmlContentPropertyHelper;
103
104import java.io.IOException;
105import java.text.DateFormat;
106import java.util.ArrayList;
107import java.util.Collection;
108import java.util.Collections;
109import java.util.Date;
110import java.util.HashMap;
111import java.util.HashSet;
112import java.util.LinkedHashMap;
113import java.util.LinkedHashSet;
114import java.util.List;
115import java.util.Locale;
116import java.util.Map;
117import java.util.Map.Entry;
118import java.util.Set;
119import java.util.function.Function;
120
121import javax.servlet.ServletException;
122import javax.servlet.http.HttpServletRequest;
123import javax.servlet.http.HttpServletResponse;
124
125import org.apache.commons.logging.Log;
126
127import org.jsoup.Jsoup;
128import org.jsoup.nodes.Document;
129import org.jsoup.select.Elements;
130
131import com.google.common.base.Supplier;
132import com.google.common.base.Suppliers;
133import com.google.common.collect.Sets;
134
135/**
136 * Utility class to generate the element data objects used within the container-page editor.<p>
137 *
138 * @since 8.0.0
139 */
140public class CmsElementUtil {
141
142    /** The maximum number of nested container levels. */
143    public static final int MAX_NESTING_LEVEL = 7;
144
145    /** Static reference to the log. */
146    private static final Log LOG = CmsLog.getLog(org.opencms.ade.containerpage.CmsElementUtil.class);
147
148    /** The ADE configuration data for the current page URI. */
149    private CmsADEConfigData m_adeConfig;
150
151    /** The cms context. */
152    private CmsObject m_cms;
153
154    /** The current page uri. */
155    private String m_currentPageUri;
156
157    /** The content locale. */
158    private Locale m_locale;
159
160    /** The current container page. */
161    private CmsResource m_page;
162
163    /** The request parameters to use while rendering the elements. */
164    @SuppressWarnings("unused")
165    private Map<String, Object> m_parameterMap;
166
167    /** The http request. */
168    private HttpServletRequest m_req;
169
170    /** The http response. */
171    private HttpServletResponse m_res;
172
173    /** The standard context bean. */
174    private CmsJspStandardContextBean m_standardContext;
175
176    /**
177     * Creates a new instance.<p>
178     * Use this constructor to set the current container page state.<p>
179     *
180     * @param cms the cms context
181     * @param currentPageUri the current page uri
182     * @param containerPage the container page bean with the current container state
183     * @param detailContentId the detail content structure id
184     * @param req the http request
185     * @param res the http response
186     * @param isDragMode if the page is in drag mode
187     * @param locale the content locale
188     *
189     * @throws CmsException if something goes wrong
190     */
191    public CmsElementUtil(
192        CmsObject cms,
193        String currentPageUri,
194        CmsContainerPageBean containerPage,
195        CmsUUID detailContentId,
196        HttpServletRequest req,
197        HttpServletResponse res,
198        boolean isDragMode,
199        Locale locale)
200    throws CmsException {
201
202        m_cms = OpenCms.initCmsObject(cms);
203        String contextPath = (String)cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH);
204        if (contextPath != null) {
205            m_cms.getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH, contextPath);
206        }
207        m_req = req;
208        m_res = res;
209        m_currentPageUri = currentPageUri;
210        m_locale = locale;
211        // initializing request for standard context bean
212        req.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, m_cms);
213        if (detailContentId != null) {
214            CmsResource detailRes = m_cms.readResource(
215                detailContentId,
216                CmsResourceFilter.ignoreExpirationOffline(m_cms));
217            req.setAttribute(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE, detailRes);
218        }
219        m_standardContext = CmsJspStandardContextBean.getInstance(req);
220        m_page = m_cms.readResource(currentPageUri, CmsResourceFilter.ignoreExpirationOffline(cms));
221        m_standardContext.setPage(containerPage);
222        m_standardContext.setDragMode(isDragMode);
223    }
224
225    /**
226     * Creates a new instance.<p>
227     *
228     * @param cms the cms context
229     * @param currentPageUri the current page uri
230     * @param detailContentId the detail content structure id
231     * @param req the http request
232     * @param res the http response
233     * @param locale the content locale
234     *
235     * @throws CmsException if something goes wrong
236     */
237    public CmsElementUtil(
238        CmsObject cms,
239        String currentPageUri,
240        CmsUUID detailContentId,
241        HttpServletRequest req,
242        HttpServletResponse res,
243        Locale locale)
244    throws CmsException {
245
246        m_cms = OpenCms.initCmsObject(cms);
247        m_req = req;
248        m_res = res;
249        m_currentPageUri = currentPageUri;
250        m_locale = locale;
251        // initializing request for standard context bean
252        req.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, m_cms);
253        if (detailContentId != null) {
254            CmsResource detailRes = m_cms.readResource(detailContentId, CmsResourceFilter.ignoreExpirationOffline(cms));
255            req.setAttribute(CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE, detailRes);
256        }
257        m_standardContext = CmsJspStandardContextBean.getInstance(req);
258        m_page = m_cms.readResource(currentPageUri);
259        CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(cms, m_page, req);
260        CmsContainerPageBean containerPage = xmlContainerPage.getContainerPage(cms);
261        m_standardContext.setPage(containerPage);
262    }
263
264    /**
265     * Creates a new instance.<p>
266     *
267     * @param cms the cms context
268     * @param currentPageUri the current page uri
269     * @param detailContentId the detail content structure id
270     * @param requestParameters the request parameters to use while rendering the elements
271     * @param req the http request
272     * @param res the http response
273     * @param locale the content locale
274     *
275     * @throws CmsException if something goes wrong
276     */
277    public CmsElementUtil(
278        CmsObject cms,
279        String currentPageUri,
280        CmsUUID detailContentId,
281        String requestParameters,
282        HttpServletRequest req,
283        HttpServletResponse res,
284        Locale locale)
285    throws CmsException {
286
287        this(cms, currentPageUri, detailContentId, req, res, locale);
288        m_parameterMap = parseRequestParameters(requestParameters);
289    }
290
291    /**
292     * Checks if a group element is allowed in a container with a given type.<p>
293     *
294     * @param containerType the container type spec (comma separated)
295     * @param groupContainer the group
296     *
297     * @return true if the group is allowed in the container
298     */
299    public static boolean checkGroupAllowed(String containerType, CmsGroupContainerBean groupContainer) {
300
301        return !Sets.intersection(CmsContainer.splitType(containerType), groupContainer.getTypes()).isEmpty();
302    }
303
304    /**
305     * Converts a client container bean to a server container bean.
306     *
307     * @param container the client container
308     * @param elements the elements of the container
309     * @return the server container bean
310     */
311    public static CmsContainerBean clientToServerContainer(
312        CmsContainer container,
313        List<CmsContainerElementBean> elements) {
314
315        return new CmsContainerBean(
316            container.getName(),
317            container.getType(),
318            container.getParentInstanceId(),
319            container.isRootContainer(),
320            container.getMaxElements(),
321            elements);
322
323    }
324
325    /**
326     * Helper method to create a string template source for a given formatter and content.
327     *
328     * @param formatter the formatter
329     * @param contentSupplier the content supplier
330     *
331     * @return the string template provider
332     */
333    public static Function<String, String> createStringTemplateSource(
334        I_CmsFormatterBean formatter,
335        Supplier<CmsXmlContent> contentSupplier) {
336
337        return key -> {
338            String result = null;
339            if (formatter != null) {
340                result = formatter.getAttributes().get(key);
341            }
342            if (result == null) {
343                CmsXmlContent content = contentSupplier.get();
344                if (content != null) {
345                    result = content.getHandler().getParameter(key);
346                }
347            }
348            return result;
349        };
350    }
351
352    /**
353     * Returns the formatter bean for the given element and container.<p>
354     *
355     * @param cms the cms context
356     * @param element the element to render
357     * @param container the container
358     * @param config the configuration data
359     * @param cache the session cache
360     *
361     * @return the formatter bean
362     */
363    public static I_CmsFormatterBean getFormatterForContainer(
364        CmsObject cms,
365        CmsContainerElementBean element,
366        CmsContainer container,
367        CmsADEConfigData config,
368        CmsADESessionCache cache) {
369
370        I_CmsFormatterBean formatter = null;
371        CmsFormatterConfiguration formatterSet = config.getFormatters(cms, element.getResource());
372        Map<String, I_CmsFormatterBean> formatters = formatterSet.getFormatterSelectionByKeyOrId(
373            container.getType(),
374            container.getWidth());
375        String formatterId = element.getIndividualSettings().get(
376            CmsFormatterConfig.getSettingsKeyForContainer(container.getName()));
377        if (formatterId != null) {
378            I_CmsFormatterBean dynamicFmt = config.findFormatter(formatterId);
379            if (dynamicFmt != null) {
380                formatter = dynamicFmt;
381            } else {
382                formatter = formatters.get(formatterId);
383            }
384        }
385        if (formatter == null) {
386            formatterId = element.getIndividualSettings().get(CmsFormatterConfig.FORMATTER_SETTINGS_KEY);
387            if (formatterId != null) {
388                formatter = lookupFormatter(config, formatterId, formatters);
389            }
390        }
391        if (formatter == null) {
392            // check for formatter config id stored for other containers matching the current container
393            for (Entry<String, String> settingsEntry : element.getIndividualSettings().entrySet()) {
394                if (settingsEntry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
395                    formatter = lookupFormatter(config, settingsEntry.getValue(), formatters);
396                    if (formatter != null) {
397                        break;
398                    }
399                }
400            }
401        }
402        if ((formatter == null) && (element.getFormatterId() != null)) {
403            for (I_CmsFormatterBean currentFormatter : formatters.values()) {
404                if ((currentFormatter.getJspStructureId() != null)
405                    && currentFormatter.getJspStructureId().equals(element.getFormatterId())) {
406                    formatter = currentFormatter;
407                    break;
408                }
409            }
410        }
411        if (formatter == null) {
412            formatter = getStartFormatter(cms, container, config, element, cache);
413        }
414        return formatter;
415    }
416
417    /**
418     * Gets the ids for the current page and potentially detail-only containers.
419     * @param cms the CMS context
420     * @param pageId the id for the current page
421     * @param detailContent the current detail content
422     * @return the set of ids for the current page and detail-only containers
423     */
424    public static Set<CmsUUID> getPageAndDetailOnlyIds(CmsObject cms, CmsUUID pageId, CmsResource detailContent) {
425
426        Set<CmsUUID> result = new HashSet<>();
427        result.add(pageId);
428        if (detailContent != null) {
429            for (CmsResource detailOnlyRes : CmsDetailOnlyContainerUtil.getDetailOnlyResources(cms, detailContent)) {
430                result.add(detailOnlyRes.getStructureId());
431            }
432        }
433        return result;
434    }
435
436    /**
437     * Checks if the given setting name is a system setting.
438     *
439     * @param name the setting name
440     * @return true if the name corresponds to a system setting
441     */
442    public static final boolean isSystemSetting(String name) {
443
444        if (CmsXmlContainerPage.LEGACY_SYSTEM_SETTING_NAMES.contains(name)
445            || CmsContainerElement.ELEMENT_INSTANCE_ID.equals(name)
446            || name.startsWith(CmsXmlContainerPage.SYSTEM_SETTING_PREFIX)
447            || name.startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
448            return true;
449        }
450        return false;
451    }
452
453    /**
454     * Returns the start formatter for a newly dropped element.<p>
455     * This will be either the least recently used matching formatter or the default formatter.<p>
456     *
457     * @param cms the cms context
458     * @param cnt the container
459     * @param configData the configuration data
460     * @param element the container element
461     * @param cache the session cache
462     *
463     * @return the formatter bean
464     */
465    private static I_CmsFormatterBean getStartFormatter(
466        CmsObject cms,
467        CmsContainer cnt,
468        CmsADEConfigData configData,
469        CmsContainerElementBean element,
470        CmsADESessionCache cache) {
471
472        I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
473        I_CmsFormatterBean formatter = cache.getRecentFormatter(type.getTypeName(), cnt, configData);
474        if (formatter == null) {
475            formatter = configData.getFormatters(cms, element.getResource()).getDefaultFormatter(
476                cnt.getType(),
477                cnt.getWidth());
478        }
479        return formatter;
480    }
481
482    /**
483     * Helper method for looking up the correct formatter for an element.
484     *
485     * @param config the sitemap config
486     * @param keyOrId the key or id of the formatter stored with the container element
487     * @param active map of active formatters by key or id
488     *
489     * @return the formatter with the given key or id
490     */
491    private static I_CmsFormatterBean lookupFormatter(
492        CmsADEConfigData config,
493        String keyOrId,
494        Map<String, I_CmsFormatterBean> active) {
495
496        I_CmsFormatterBean dynamicFmt = config.findFormatter(keyOrId);
497        if (dynamicFmt != null) {
498            for (String key : new String[] {dynamicFmt.getKey(), dynamicFmt.getId(), keyOrId}) {
499                if ((key != null) && (active.get(key) != null)) {
500                    return active.get(key);
501                }
502            }
503            return null;
504        } else {
505            // schema formatters
506            return active.get(keyOrId);
507        }
508    }
509
510    /**
511     * Returns the HTML content for the given resource and container.<p>
512     *
513     * @param elementFile the element resource file
514     * @param elementId the element id
515     * @param container the container
516     *
517     * @return the HTML content
518     */
519    public String getContentByContainer(CmsFile elementFile, String elementId, CmsContainer container) {
520
521        CmsContainerElementBean element = CmsADESessionCache.getCache(m_req, m_cms).getCacheContainerElement(elementId);
522        element = element.clone();
523        element.setTemporaryFile(elementFile);
524        CmsFormatterConfiguration configs = getFormatterConfiguration(element.getResource());
525        return getContentByContainer(element, container, configs);
526    }
527
528    /**
529     * Returns the data for an element.<p>
530     *
531     * @param page the current container page
532     * @param element the resource
533     * @param containers the containers on the current container page
534     *
535     * @return the data for an element
536     *
537     * @throws CmsException if something goes wrong
538     */
539    public CmsContainerElementData getElementData(
540        CmsResource page,
541        CmsContainerElementBean element,
542        Collection<CmsContainer> containers)
543    throws CmsException {
544
545        Locale requestLocale = m_cms.getRequestContext().getLocale();
546        m_cms.getRequestContext().setLocale(m_locale);
547        element.initResource(m_cms);
548        if (element.getResource().isFolder()) {
549            return null;
550        }
551        boolean typeDisabled = false;
552        boolean createDisabled = false;
553        if (page != null) {
554
555            CmsADEConfigData config = OpenCms.getADEManager().lookupConfigurationWithCache(m_cms, page.getRootPath());
556            String typeName = OpenCms.getResourceManager().getResourceType(element.getResource()).getTypeName();
557            if (!config.getAddableTypeNames().contains(typeName)) {
558                typeDisabled = true;
559            }
560            CmsResourceTypeConfig typeConfig = config.getTypesByName().get(typeName);
561            if ((typeConfig == null) || typeConfig.isCreateDisabled()) {
562                createDisabled = true;
563            }
564        }
565
566        CmsContainerElementData elementData = getBaseElementData(page, element);
567        elementData.setAddDisabled(typeDisabled);
568        elementData.setCopyDisabled(createDisabled);
569        CmsFormatterConfiguration formatterConfiguraton = getFormatterConfiguration(element.getResource());
570        Map<String, String> contents = new HashMap<String, String>();
571        if (element.isGroupContainer(m_cms)) {
572            Map<String, CmsContainer> containersByName = new HashMap<String, CmsContainer>();
573            for (CmsContainer container : containers) {
574                containersByName.put(container.getName(), container);
575            }
576            CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(
577                m_cms,
578                element.getResource(),
579                m_req);
580            CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(m_cms);
581            // make sure to use the content title and not the property title
582            elementData.setTitle(groupContainer.getTitle());
583            elementData.setTypes(groupContainer.getTypes());
584            elementData.setDescription(groupContainer.getDescription());
585            if (groupContainer.getTypes().isEmpty()) {
586                if (groupContainer.getElements().isEmpty()) {
587                    String emptySub = "<div>NEW AND EMPTY</div>";
588                    for (CmsContainer cont : containersByName.values()) {
589                        if (formatterConfiguraton.hasFormatter(cont.getType(), cont.getWidth())) {
590                            contents.put(cont.getName(), emptySub);
591                        }
592                    }
593                } else {
594                    // TODO: throw appropriate exception
595                    return null;
596                }
597            } else {
598
599                // add formatter and content entries for the supported types
600                for (CmsContainer cnt : containersByName.values()) {
601
602                    String type = cnt.getType();
603                    if (checkGroupAllowed(type, groupContainer)) {
604                        contents.put(cnt.getName(), "<div>should not be used</div>");
605                    }
606                }
607            }
608            // add subitems
609            List<String> subItems = new ArrayList<String>();
610
611            for (CmsContainerElementBean subElement : groupContainer.getElements()) {
612                // collect ids
613                subItems.add(subElement.editorHash());
614            }
615            elementData.setSubItems(subItems);
616        } else if (element.isInheritedContainer(m_cms)) {
617            CmsInheritanceReferenceParser parser = new CmsInheritanceReferenceParser(m_cms);
618            parser.parse(element.getResource());
619            CmsInheritanceReference ref = parser.getReference(m_locale);
620            String name = null;
621            // check for new inheritance reference
622            if (ref != null) {
623                name = ref.getName();
624                elementData.setDescription(ref.getDescription());
625                elementData.setTitle(ref.getTitle());
626            }
627            for (CmsContainer container : containers) {
628                if (formatterConfiguraton.hasFormatter(container.getType(), container.getWidth())) {
629                    contents.put(container.getName(), "<div>should not be used</div>");
630                }
631            }
632
633            List<CmsInheritanceInfo> inheritanceInfos = new ArrayList<CmsInheritanceInfo>();
634            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(name)) {
635                CmsInheritedContainerState result = OpenCms.getADEManager().getInheritedContainerState(
636                    m_cms,
637                    m_cms.addSiteRoot(m_currentPageUri),
638                    name);
639                for (CmsContainerElementBean subElement : result.getElements(true)) {
640                    CmsInheritanceInfo inheritanceInfo = subElement.getInheritanceInfo();
641                    inheritanceInfo.setClientId(subElement.editorHash());
642                    inheritanceInfos.add(inheritanceInfo);
643                }
644            } else {
645                // setting a new id for name, will be persisted once the inheritance reference is edited and saved.
646                // use the structure id so it will always be the same for the resource
647                name = element.getResource().getStructureId().toString();
648            }
649            elementData.setInheritanceInfos(inheritanceInfos);
650            elementData.setInheritanceName(name);
651        } else {
652            for (CmsContainer cnt : containers) {
653                boolean missesFormatterSetting = !elementData.getSettings().containsKey(
654                    CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()));
655                if (missesFormatterSetting) {
656                    if (element.getFormatterId() == null) {
657                        I_CmsFormatterBean formatter = getStartFormatter(
658                            m_cms,
659                            cnt,
660                            m_adeConfig,
661                            element,
662                            CmsADESessionCache.getCache(m_req, m_cms));
663                        if (formatter != null) {
664                            elementData.getSettings().put(
665                                CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()),
666                                formatter.getId());
667                            element.addFormatterSetting(cnt.getName(), formatter.getKeyOrId());
668                        }
669                    } else {
670                        Map<String, I_CmsFormatterBean> formatterSelection = formatterConfiguraton.getFormatterSelection(
671                            cnt.getType(),
672                            cnt.getWidth());
673                        for (Entry<String, I_CmsFormatterBean> formatterEntry : formatterSelection.entrySet()) {
674                            I_CmsFormatterBean formatter = formatterEntry.getValue();
675                            if (element.getFormatterId().equals(formatter.getJspStructureId())) {
676                                elementData.getSettings().put(
677                                    CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()),
678                                    formatter.getKeyOrId());
679                                break;
680                            }
681                        }
682                    }
683                }
684            }
685            // get the formatter configuration
686            Map<String, String> contentsByName = getContentsByContainerName(element, containers);
687            contents = contentsByName;
688        }
689        CmsListInfoBean listInfo = CmsVfsService.getPageInfo(m_cms, element.getResource());
690        elementData.setListInfo(listInfo);
691        elementData.setContents(contents);
692        m_cms.getRequestContext().setLocale(requestLocale);
693        return elementData;
694    }
695
696    /**
697     * Returns the formatter and settings config data for an element.<p>
698     *
699     * @param page the current container page
700     * @param element the resource
701     * @param containerId the parent container id
702     * @param containers the containers on the current container page
703     *
704     * @return the data for an element
705     *
706     * @throws CmsException if something goes wrong
707     */
708    public CmsElementSettingsConfig getElementSettingsConfig(
709        CmsResource page,
710        CmsContainerElementBean element,
711        String containerId,
712        Collection<CmsContainer> containers)
713    throws CmsException {
714
715        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
716        CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfigurationWithCache(m_cms, page.getRootPath());
717        boolean isCopyGroup = CmsResourceTypeXmlContainerPage.isModelCopyGroup(m_cms, page);
718        Locale requestLocale = m_cms.getRequestContext().getLocale();
719        m_cms.getRequestContext().setLocale(m_locale);
720        element.initResource(m_cms);
721        if (element.getResource().isFolder()) {
722            return null;
723        }
724        String schema = null;
725        try {
726            I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(element.getResource());
727            schema = type.getConfiguration().getString("schema", null);
728        } catch (Exception e) {
729            LOG.error(e.getLocalizedMessage(), e);
730        }
731        CmsContainerElementData elementData = getBaseElementData(page, element);
732
733        Map<String, String> settingUpdates = new HashMap<>();
734        for (Map.Entry<String, String> entry : elementData.getSettings().entrySet()) {
735            int underscorePos = entry.getKey().indexOf("_");
736            if ((underscorePos >= 0) && !isSystemSetting(entry.getKey())) {
737                String prefix = entry.getKey().substring(0, underscorePos);
738                I_CmsFormatterBean dynamicFmt = adeConfig.findFormatter(prefix, true);
739
740                if (CmsUUID.isValidUUID(prefix)) {
741                    // If we already have a formatter referenced by name, we don't need to do anything
742                    if ((dynamicFmt != null) && (dynamicFmt.getKey() != null)) {
743                        // Replace setting prefix with formatter key
744                        String newSettingName = dynamicFmt.getKey() + entry.getKey().substring(underscorePos);
745                        settingUpdates.put(newSettingName, entry.getValue());
746                        settingUpdates.put(entry.getKey(), null);
747                    }
748                }
749            }
750
751            if (entry.getKey().startsWith(CmsFormatterConfig.FORMATTER_SETTINGS_KEY)) {
752                String value = entry.getValue();
753                if (CmsUUID.isValidUUID(value)) {
754                    I_CmsFormatterBean dynamicFmt = adeConfig.findFormatter(value);
755                    if ((dynamicFmt != null) && (dynamicFmt.getKey() != null)) {
756                        settingUpdates.put(entry.getKey(), dynamicFmt.getKey());
757                    }
758                }
759            }
760        }
761        for (String key : settingUpdates.keySet()) {
762            String value = settingUpdates.get(key);
763            if (value == null) {
764                elementData.getSettings().remove(key);
765            } else {
766                elementData.getSettings().put(key, value);
767            }
768        }
769        Supplier<CmsXmlContent> contentSupplier = Suppliers.memoize(() -> {
770            try {
771                return CmsXmlContentFactory.unmarshal(m_cms, m_cms.readFile(element.getResource()));
772            } catch (CmsException e) {
773                LOG.error(e.getLocalizedMessage(), e);
774                return null;
775            }
776        });
777        I_CmsFormatterBean foundFormatter = null;
778
779        if (!element.isGroupContainer(m_cms) && !element.isInheritedContainer(m_cms)) {
780            CmsFormatterConfiguration formatterConfiguraton = getFormatterConfiguration(element.getResource());
781            Map<String, CmsFormatterConfigCollection> formatters = new HashMap<String, CmsFormatterConfigCollection>();
782            for (CmsContainer cnt : containers) {
783                if (cnt.getName().equals(containerId)) {
784                    CmsFormatterConfigCollection containerFormatters = new CmsFormatterConfigCollection();
785                    String foundFormatterKey = null;
786                    for (String containerName : new String[] {cnt.getName(), ""}) {
787                        foundFormatterKey = elementData.getSettings().get(
788                            CmsFormatterConfig.getSettingsKeyForContainer(containerName));
789                        if (foundFormatterKey != null) {
790                            break;
791                        }
792                    }
793                    boolean missesFormatterSetting = (foundFormatterKey == null);
794                    if (!missesFormatterSetting) {
795                        foundFormatter = adeConfig.findFormatter(foundFormatterKey);
796                    }
797                    Map<String, I_CmsFormatterBean> formatterSelection = formatterConfiguraton.getFormatterSelection(
798                        cnt.getType(),
799                        cnt.getWidth());
800                    for (Entry<String, I_CmsFormatterBean> formatterEntry : formatterSelection.entrySet()) {
801                        I_CmsFormatterBean formatter = formatterEntry.getValue();
802                        String id = formatterEntry.getValue().getId();
803                        if (missesFormatterSetting
804                            && ((element.getFormatterId() == null)
805                                || element.getFormatterId().equals(formatter.getJspStructureId()))) {
806                            elementData.getSettings().put(
807                                CmsFormatterConfig.getSettingsKeyForContainer(cnt.getName()),
808                                id);
809                            missesFormatterSetting = false;
810                        }
811                        String label = formatter.getNiceName(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms));
812                        if (formatterEntry.getKey().startsWith(CmsFormatterConfig.SCHEMA_FORMATTER_ID)) {
813                            label = Messages.get().getBundle().key(Messages.GUI_SCHEMA_FORMATTER_LABEL_0)
814                                + " ["
815                                + CmsResource.getName(formatter.getJspRootPath())
816                                + "]";
817                        }
818                        if (CmsStringUtil.isEmptyOrWhitespaceOnly(label)) {
819                            label = id;
820                        }
821                        CmsFormatterConfig config = new CmsFormatterConfig(id);
822                        Set<String> cssResources = new LinkedHashSet<String>();
823                        for (String cssSitePath : formatter.getCssHeadIncludes()) {
824                            cssResources.add(OpenCms.getLinkManager().getOnlineLink(m_cms, cssSitePath));
825                        }
826                        config.setCssResources(cssResources);
827                        config.setInlineCss(formatter.getInlineCss());
828                        config.setKey(formatter.getKey());
829                        config.setLabel(label);
830                        config.setDescription(
831                            formatter.getDescription(OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms)));
832                        Map<String, CmsXmlContentProperty> settingsConfig = OpenCms.getADEManager().getFormatterSettings(
833                            m_cms,
834                            adeConfig,
835                            formatter,
836                            element.getResource(),
837                            m_locale,
838                            m_req);
839                        Function<String, String> templateSource = createStringTemplateSource(
840                            formatter,
841                            contentSupplier);
842                        settingsConfig = CmsXmlContentPropertyHelper.resolveMacrosForPropertyInfo(
843                            m_cms,
844                            page,
845                            element.getResource(),
846                            contentSupplier,
847                            templateSource,
848                            settingsConfig);
849                        config.setSettingConfig(settingsConfig);
850                        List<I_CmsFormatterBean> nestedFormatters = OpenCms.getADEManager().getNestedFormatters(
851                            m_cms,
852                            adeConfig,
853                            element.getResource(),
854                            m_locale,
855                            m_req);
856                        if ((nestedFormatters != null) && !nestedFormatters.isEmpty()) {
857                            Map<String, String> settingPrefixes = new LinkedHashMap<String, String>();
858                            for (I_CmsFormatterBean nested : nestedFormatters) {
859                                String sectionLabel = nested.getNiceName(
860                                    OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms));
861                                settingPrefixes.put(nested.getId(), sectionLabel);
862                                if (nested.getKey() != null) {
863                                    settingPrefixes.put(nested.getKey() + "_", sectionLabel);
864                                }
865                            }
866                            config.setNestedFormatterPrefixes(settingPrefixes);
867                        }
868
869                        config.setJspRootPath(formatter.getJspRootPath());
870                        containerFormatters.add(config);
871                    }
872                    formatters.put(cnt.getName(), containerFormatters);
873                }
874            }
875            elementData.setFormatters(formatters);
876        }
877
878        m_cms.getRequestContext().setLocale(requestLocale);
879        ArrayList<CmsAdditionalInfoBean> infos = new ArrayList<>();
880
881        CmsResource resource = element.getResource();
882        String resTypeName = OpenCms.getResourceManager().getResourceType(resource).getTypeName();
883        CmsExplorerTypeSettings cmsExplorerTypeSettings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(
884            resTypeName);
885        if (null == cmsExplorerTypeSettings) {
886            CmsMessageContainer errMsg = org.opencms.gwt.Messages.get().container(
887                org.opencms.gwt.Messages.ERR_EXPLORER_TYPE_SETTINGS_FOR_RESOURCE_TYPE_NOT_FOUND_3,
888                resource.getRootPath(),
889                resTypeName,
890                Integer.valueOf(resource.getTypeId()));
891            throw new CmsConfigurationException(errMsg);
892        }
893        String key = cmsExplorerTypeSettings.getKey();
894        Locale currentLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
895        CmsMessages messages = OpenCms.getWorkplaceManager().getMessages(currentLocale);
896        String resTypeNiceName = messages.key(key);
897        infos.add(
898            new CmsAdditionalInfoBean(
899                messages.key(org.opencms.workplace.commons.Messages.GUI_LABEL_TYPE_0),
900                resTypeNiceName,
901                null));
902
903        try {
904            CmsRelationFilter filter = CmsRelationFilter.relationsFromStructureId(
905                element.getResource().getStructureId()).filterType(CmsRelationType.XSD);
906            for (CmsRelation relation : m_cms.readRelations(filter)) {
907                CmsResource target = relation.getTarget(m_cms, CmsResourceFilter.IGNORE_EXPIRATION);
908                String label = Messages.get().getBundle(wpLocale).key(Messages.GUI_ADDINFO_SCHEMA_0);
909                infos.add(new CmsAdditionalInfoBean(label, target.getRootPath(), null));
910                break;
911            }
912        } catch (CmsException e) {
913            LOG.error(e.getLocalizedMessage(), e);
914        }
915        if (foundFormatter != null) {
916            Map<String, String> formatterInfo = CmsDefaultResourceStatusProvider.getFormatterInfo(
917                m_cms,
918                foundFormatter);
919            for (Map.Entry<String, String> entry : formatterInfo.entrySet()) {
920                infos.add(new CmsAdditionalInfoBean(entry.getKey(), entry.getValue(), null));
921            }
922        }
923
924        CmsResourceState state = element.getResource().getState();
925        return new CmsElementSettingsConfig(elementData, state, infos, schema, isCopyGroup);
926    }
927
928    /**
929     * Gets the container page.<p>
930     *
931     * @return the container page resource
932     */
933    public CmsResource getPage() {
934
935        return m_page;
936    }
937
938    /**
939     * Sets the data to the given container element.<p>
940     *
941     * @param elementBean the element bean
942     * @param result the container element to set the data to
943     *
944     * @return the container element
945     *
946     * @throws CmsException if something goes wrong
947     */
948    public CmsContainerElement setElementInfo(CmsContainerElementBean elementBean, CmsContainerElement result)
949    throws CmsException {
950
951        Locale wpLocale = OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms);
952        // reinitializing resource to avoid caching issues
953        elementBean.initResource(m_cms);
954        CmsResource resource = elementBean.getResource();
955        boolean isModelGroup = elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_ID);
956        if (isModelGroup) {
957            CmsUUID groupId = new CmsUUID(elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID));
958            resource = m_cms.readResource(groupId, CmsResourceFilter.IGNORE_EXPIRATION);
959        }
960
961        boolean newEditorDisabled = !CmsWorkplaceEditorManager.checkAcaciaEditorAvailable(
962            m_cms,
963            elementBean.getResource());
964        result.setNewEditorDisabled(newEditorDisabled);
965        I_CmsResourceType resourceType = OpenCms.getResourceManager().getResourceType(resource);
966        String typeName = resourceType.getTypeName();
967        result.setHasEditHandler(
968            (resourceType instanceof CmsResourceTypeXmlContent)
969                && (((CmsResourceTypeXmlContent)resourceType).getEditHandler(m_cms) != null));
970        result.setResourceType(typeName);
971        result.setIconClasses(
972            CmsIconUtil.getIconClasses(
973                CmsIconUtil.getDisplayType(m_cms, resource),
974                elementBean.getResource().getName(),
975                false));
976        CmsPermissionInfo permissionInfo;
977        String title;
978        String subTitle;
979        if (!elementBean.isInMemoryOnly()) {
980            CmsElementLockInfo lockInfo = getLockInfo(m_cms, resource);
981            result.setLockInfo(lockInfo);
982            permissionInfo = OpenCms.getADEManager().getPermissionInfo(m_cms, resource, m_page.getRootPath());
983            if (CmsResourceTypeXmlContent.isXmlContent(resource)) {
984                if (CmsStringUtil.isEmptyOrWhitespaceOnly(permissionInfo.getNoEditReason())
985                    && elementBean.isInheritedContainer(m_cms)) {
986                    String requestUri = m_cms.getRequestContext().getUri();
987                    String folderPath = CmsResource.getFolderPath(requestUri);
988                    String configPath = CmsStringUtil.joinPaths(
989                        folderPath,
990                        CmsContainerConfigurationCache.INHERITANCE_CONFIG_FILE_NAME);
991                    if (m_cms.existsResource(configPath)) {
992                        permissionInfo.setNoEditReason(
993                            new CmsResourceUtil(m_cms, m_cms.readResource(configPath)).getNoEditReason(wpLocale, true));
994                    } else {
995                        if (!m_cms.getLock(folderPath).isLockableBy(m_cms.getRequestContext().getCurrentUser())) {
996                            permissionInfo.setNoEditReason(
997                                org.opencms.workplace.explorer.Messages.get().getBundle(wpLocale).key(
998                                    org.opencms.workplace.explorer.Messages.GUI_NO_EDIT_REASON_LOCK_1,
999                                    new CmsResourceUtil(m_cms, m_cms.readResource(folderPath)).getLockedByName()));
1000                        }
1001                    }
1002                }
1003            } else {
1004                permissionInfo.setNoEditReason(
1005                    Messages.get().getBundle(wpLocale).key(Messages.GUI_ELEMENT_RESOURCE_CAN_NOT_BE_EDITED_0));
1006            }
1007            CmsGallerySearchResult searchResult = CmsGallerySearch.searchById(
1008                m_cms,
1009                resource.getStructureId(),
1010                m_locale);
1011            title = searchResult.getTitle();
1012            if (CmsStringUtil.isEmptyOrWhitespaceOnly(title)) {
1013                resource.getName();
1014            }
1015            subTitle = searchResult.getUserLastModified();
1016            Date lastModDate = searchResult.getDateLastModified();
1017            if (lastModDate != null) {
1018                subTitle += " / " + CmsDateUtil.getDateTime(lastModDate, DateFormat.MEDIUM, wpLocale);
1019            }
1020        } else {
1021            permissionInfo = new CmsPermissionInfo(true, true, "");
1022            title = CmsWorkplaceMessages.getResourceTypeName(wpLocale, typeName);
1023            subTitle = CmsWorkplaceMessages.getResourceTypeDescription(wpLocale, typeName);
1024        }
1025        result.setTitle(title);
1026        result.setSubTitle(subTitle);
1027        result.setClientId(elementBean.editorHash());
1028        result.setSitePath(m_cms.getSitePath(resource));
1029
1030        result.setCreateNew(elementBean.isCreateNew());
1031        CmsResourceTypeConfig typeConfig = getConfigData().getResourceType(typeName);
1032        if (!elementBean.isInMemoryOnly() && (typeConfig != null)) {
1033            result.setCopyInModels(typeConfig.isCopyInModels());
1034            if (typeConfig.isCheckReuse()) {
1035                final Set<CmsUUID> pageAndAttachments = getPageAndDetailOnlyIds();
1036                boolean reused = OpenCms.getADEManager().isElementReused(
1037                    resource,
1038                    res -> pageAndAttachments.contains(res.getStructureId()));
1039                result.setReused(reused);
1040            }
1041        }
1042
1043        Map<CmsUUID, CmsElementView> viewMap = OpenCms.getADEManager().getElementViews(m_cms);
1044
1045        boolean isModelGroupEditing = CmsModelGroupHelper.isModelGroupResource(m_page);
1046        if (!isModelGroup
1047            && isModelGroupEditing
1048            && elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_STATE)
1049            && (ModelGroupState.isModelGroup == ModelGroupState.evaluate(
1050                elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE)))) {
1051            isModelGroup = true;
1052        }
1053        if (!isModelGroupEditing && isModelGroup) {
1054            CmsResourceTypeConfig modelGroupConfig = getConfigData().getResourceType(
1055                CmsResourceTypeXmlContainerPage.MODEL_GROUP_TYPE_NAME);
1056            if (modelGroupConfig != null) {
1057                CmsUUID elementView = modelGroupConfig.getElementView();
1058                CmsElementView viewObject = viewMap.get(elementView);
1059                if ((viewObject != null) && (viewObject.getParentViewId() != null)) {
1060                    elementView = viewObject.getParentViewId();
1061                }
1062                result.setElementView(elementView);
1063            }
1064        } else if (typeConfig != null) {
1065            CmsUUID elementView = typeConfig.getElementView();
1066            CmsElementView viewObject = viewMap.get(elementView);
1067            if ((viewObject != null) && (viewObject.getParentViewId() != null)) {
1068                elementView = viewObject.getParentViewId();
1069            }
1070            result.setElementView(elementView);
1071        }
1072        if (CmsStringUtil.isEmptyOrWhitespaceOnly(permissionInfo.getNoEditReason())
1073            && ((typeConfig == null) || typeConfig.isEditDisabled())) {
1074            String message = Messages.get().getBundle(wpLocale).key(
1075                Messages.GUI_CONTAINERPAGE_EDIT_DISABLED_BY_SITEMAP_CONFIG_0);
1076            permissionInfo.setNoEditReason(message);
1077        }
1078        result.setHasSettings(hasSettings(m_cms, elementBean.getResource()));
1079        result.setPermissionInfo(permissionInfo);
1080        result.setReleasedAndNotExpired(elementBean.isReleasedAndNotExpired());
1081        if (elementBean.isModelGroup()) {
1082            String modelId = elementBean.getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_ID);
1083            result.setModelGroupId(modelId != null ? new CmsUUID(modelId) : CmsUUID.getNullUUID());
1084        }
1085        result.setWasModelGroup(
1086            elementBean.getIndividualSettings().containsKey(CmsContainerElement.MODEL_GROUP_STATE)
1087                && (ModelGroupState.evaluate(
1088                    elementBean.getIndividualSettings().get(
1089                        CmsContainerElement.MODEL_GROUP_STATE)) == ModelGroupState.wasModelGroup));
1090        return result;
1091    }
1092
1093    /**
1094     * Returns the formatter configuration for the given element resource.<p>
1095     *
1096     * @param resource the element resource
1097     *
1098     * @return the formatter configuration
1099     */
1100    CmsFormatterConfiguration getFormatterConfiguration(CmsResource resource) {
1101
1102        return getConfigData().getFormatters(m_cms, resource);
1103    }
1104
1105    /**
1106     * Returns the base element data for the given element bean, without content or formatter info.<p>
1107     *
1108     * @param page the current container page
1109     * @param element the resource
1110     *
1111     * @return base element data
1112     *
1113     * @throws CmsException in case reading the data fails
1114     */
1115    private CmsContainerElementData getBaseElementData(CmsResource page, CmsContainerElementBean element)
1116    throws CmsException {
1117
1118        CmsResourceUtil resUtil = new CmsResourceUtil(m_cms, element.getResource());
1119        CmsContainerElementData elementData = new CmsContainerElementData();
1120        setElementInfo(element, elementData);
1121        elementData.setLoadTime(System.currentTimeMillis());
1122        elementData.setLastModifiedDate(element.getResource().getDateLastModified());
1123        String userName = null;
1124        try {
1125            CmsUser user = m_cms.readUser(element.getResource().getUserLastModified());
1126            userName = user.getName();
1127        } catch (CmsException e) {
1128            userName = "" + element.getResource().getUserLastModified();
1129            LOG.debug(e.getLocalizedMessage(), e);
1130        }
1131        elementData.setLastModifiedByUser(userName);
1132        elementData.setNavText(resUtil.getNavText());
1133        Map<String, CmsXmlContentProperty> settingConfig = CmsXmlContentPropertyHelper.getPropertyInfo(
1134            m_cms,
1135            page,
1136            element.getResource());
1137        elementData.setSettings(
1138            CmsXmlContentPropertyHelper.convertPropertiesToClientFormat(
1139                m_cms,
1140                element.getIndividualSettings(),
1141                settingConfig));
1142        return elementData;
1143    }
1144
1145    /**
1146     * Returns the ADE configuration data for the current URI.<p>
1147     *
1148     * @return the ADE configuration data
1149     */
1150    private CmsADEConfigData getConfigData() {
1151
1152        if (m_adeConfig == null) {
1153            m_adeConfig = OpenCms.getADEManager().lookupConfigurationWithCache(
1154                m_cms,
1155                m_cms.addSiteRoot(m_currentPageUri));
1156        }
1157        return m_adeConfig;
1158    }
1159
1160    /**
1161     * Returns the HTML content of the given element and container.<p>
1162     *
1163     * @param element the element
1164     * @param container the container
1165     * @param configs the formatter configurations
1166     *
1167     * @return the HTML content
1168     */
1169    private String getContentByContainer(
1170        CmsContainerElementBean element,
1171        CmsContainer container,
1172        CmsFormatterConfiguration configs) {
1173
1174        String content = null;
1175
1176        I_CmsFormatterBean formatter = getFormatterForContainer(
1177            m_cms,
1178            element,
1179            container,
1180            m_adeConfig,
1181            CmsADESessionCache.getCache(m_req, m_cms));
1182        if (formatter != null) {
1183            element = element.clone(); // clone element because presets for different containers may be different
1184            element.initSettings(m_cms, m_adeConfig, formatter, m_locale, m_req, container.getSettingPresets());
1185            try {
1186                content = getElementContent(element, m_cms.readResource(formatter.getJspStructureId()), container);
1187                //                }
1188            } catch (Exception e) {
1189                LOG.error(e.getLocalizedMessage(), e);
1190            }
1191            if (content != null) {
1192                content = removeScriptTags(content);
1193            }
1194        }
1195        return content;
1196    }
1197
1198    /**
1199     * Returns the rendered element content for all the given containers.
1200     *
1201     * @param element the element to render
1202     * @param containers the containers the element appears in
1203     *
1204     * @return a map from container names to rendered page contents
1205     */
1206    private Map<String, String> getContentsByContainerName(
1207        CmsContainerElementBean element,
1208        Collection<CmsContainer> containers) {
1209
1210        CmsFormatterConfiguration configs = getFormatterConfiguration(element.getResource());
1211        Map<String, String> result = new HashMap<String, String>();
1212        for (CmsContainer container : containers) {
1213            String content = getContentByContainer(element, container, configs);
1214            if (content != null) {
1215                content = removeScriptTags(content);
1216            }
1217            result.put(container.getName(), content);
1218        }
1219        return result;
1220    }
1221
1222    /**
1223     * Returns the content of an element when rendered with the given formatter.<p>
1224     *
1225     * @param element the element bean
1226     * @param formatter the formatter uri
1227     * @param container the container for which the element content should be retrieved
1228     *
1229     * @return generated html code
1230     *
1231     * @throws CmsException if an cms related error occurs
1232     * @throws ServletException if a jsp related error occurs
1233     *
1234     * @throws IOException if a jsp related error occurs
1235     */
1236    private String getElementContent(CmsContainerElementBean element, CmsResource formatter, CmsContainer container)
1237    throws CmsException, ServletException, IOException {
1238
1239        element.initResource(m_cms);
1240        TemplateBean templateBean = CmsADESessionCache.getCache(m_req, m_cms).getTemplateBean(
1241            m_cms.addSiteRoot(m_currentPageUri),
1242            true);
1243        String oldUri = m_cms.getRequestContext().getUri();
1244        try {
1245            m_cms.getRequestContext().setUri(m_currentPageUri);
1246            CmsContainerBean containerBean = null;
1247            if ((m_standardContext.getPage() != null)
1248                && m_standardContext.getPage().getContainers().containsKey(container.getName())) {
1249                containerBean = m_standardContext.getPage().getContainers().get(container.getName());
1250            } else {
1251                containerBean = CmsElementUtil.clientToServerContainer(
1252                    container,
1253                    Collections.<CmsContainerElementBean> emptyList());
1254            }
1255            if (containerBean.getWidth() == null) {
1256                containerBean.setWidth(String.valueOf(container.getWidth()));
1257            }
1258            containerBean.setDetailOnly(container.isDetailOnly());
1259            m_standardContext.setContainer(containerBean);
1260            m_standardContext.setElement(element);
1261            m_standardContext.setEdited(true);
1262            // to enable 'old' direct edit features for content-collector-elements,
1263            // set the direct-edit-provider-attribute in the request
1264            I_CmsDirectEditProvider eb = new CmsAdvancedDirectEditProvider();
1265            eb.init(m_cms, CmsDirectEditMode.TRUE, element.getSitePath());
1266            m_req.setAttribute(I_CmsDirectEditProvider.ATTRIBUTE_DIRECT_EDIT_PROVIDER, eb);
1267            m_req.setAttribute(CmsTemplateContextManager.ATTR_TEMPLATE_BEAN, templateBean);
1268            String encoding = m_res.getCharacterEncoding();
1269            return (new String(
1270                OpenCms.getResourceManager().getLoader(formatter).dump(m_cms, formatter, null, m_locale, m_req, m_res),
1271                encoding)).trim();
1272        } finally {
1273            m_cms.getRequestContext().setUri(oldUri);
1274        }
1275    }
1276
1277    /**
1278     * Gets the lock information.
1279     *
1280     * @param cms the current CMS context
1281     * @param resource the resource for which to get lock information
1282     * @return the lock information
1283     */
1284    private CmsElementLockInfo getLockInfo(CmsObject cms, CmsResource resource) {
1285
1286        try {
1287            CmsLock lock = cms.getLock(resource);
1288            CmsUUID owner = lock.getUserId();
1289            boolean isPublish = lock.isPublish();
1290            return new CmsElementLockInfo(owner, isPublish);
1291        } catch (Exception e) {
1292            LOG.error(e.getLocalizedMessage(), e);
1293            return new CmsElementLockInfo(null, false);
1294        }
1295    }
1296
1297    /**
1298     * Gets the ids for the current page and potentially detail-only containers.
1299     *
1300     * @return the set of ids for the current page and detail-only containers
1301     */
1302    private Set<CmsUUID> getPageAndDetailOnlyIds() {
1303
1304        Set<CmsUUID> result = new HashSet<>();
1305        result.add(m_page.getStructureId());
1306        CmsResource detailContent = (CmsResource)m_req.getAttribute(
1307            CmsDetailPageResourceHandler.ATTR_DETAIL_CONTENT_RESOURCE);
1308        if (detailContent != null) {
1309            for (CmsResource detailOnlyRes : CmsDetailOnlyContainerUtil.getDetailOnlyResources(m_cms, detailContent)) {
1310                result.add(detailOnlyRes.getStructureId());
1311            }
1312        }
1313        return result;
1314    }
1315
1316    /**
1317     * Helper method for checking whether there are properties defined for a given content element.<p>
1318     *
1319     * @param cms the CmsObject to use for VFS operations
1320     * @param resource the resource for which it should be checked whether it has properties
1321     *
1322     * @return true if the resource has properties defined
1323     *
1324     * @throws CmsException if something goes wrong
1325     */
1326    private boolean hasSettings(CmsObject cms, CmsResource resource) throws CmsException {
1327
1328        if (!CmsResourceTypeXmlContent.isXmlContent(resource)) {
1329            return false;
1330        }
1331
1332        CmsADEConfigData config = getConfigData();
1333        CmsFormatterConfiguration formatters = config.getFormatters(m_cms, resource);
1334        boolean result = (formatters.getAllFormatters().size() > 1)
1335            || !CmsXmlContentPropertyHelper.getPropertyInfo(m_cms, null, resource).isEmpty();
1336        if (!result && (formatters.getAllFormatters().size() == 1)) {
1337            result = (formatters.getAllFormatters().get(0).getSettings(config).size() > 0);
1338        }
1339        return result;
1340    }
1341
1342    /**
1343     * Parses the given request parameters string into a parameter map.<p>
1344     *
1345     * @param requestParameters the request parameters to parse
1346     *
1347     * @return the parameter map
1348     */
1349    private Map<String, Object> parseRequestParameters(String requestParameters) {
1350
1351        Map<String, Object> parameterMap;
1352        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(requestParameters)) {
1353            parameterMap = new HashMap<String, Object>();
1354            String[] params = requestParameters.split("&");
1355            for (int i = 0; i < params.length; i++) {
1356                int position = params[i].indexOf("=");
1357                if (position >= 0) {
1358                    String key = params[i].substring(0, position);
1359                    String value = params[i].substring(position + 1);
1360                    if (value.contains(",")) {
1361                        parameterMap.put(key, value.split(","));
1362                    } else {
1363                        parameterMap.put(key, value);
1364                    }
1365                }
1366            }
1367        } else {
1368            parameterMap = Collections.<String, Object> emptyMap();
1369        }
1370        return parameterMap;
1371    }
1372
1373    /**
1374     * Removes all script tags from given input.<p>
1375     *
1376     * @param input the input to remove script tags from
1377     *
1378     * @return the cleaned input
1379     */
1380    private String removeScriptTags(String input) {
1381
1382        Document doc = Jsoup.parseBodyFragment(input);
1383        Elements scriptTags = doc.select("script");
1384        String output = input;
1385        if (scriptTags.size() > 0) {
1386            scriptTags.remove();
1387            output = doc.body().html();
1388        }
1389        return output;
1390    }
1391}