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