001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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.configuration;
029
030import org.opencms.ade.configuration.formatters.CmsFormatterChangeSet;
031import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCache;
032import org.opencms.ade.containerpage.shared.CmsCntPageData.ElementDeleteMode;
033import org.opencms.ade.detailpage.CmsDetailPageInfo;
034import org.opencms.ade.galleries.CmsAddContentRestriction;
035import org.opencms.file.CmsFile;
036import org.opencms.file.CmsObject;
037import org.opencms.file.CmsResource;
038import org.opencms.file.CmsResourceFilter;
039import org.opencms.file.types.I_CmsResourceType;
040import org.opencms.gwt.CmsIconUtil;
041import org.opencms.i18n.CmsLocaleManager;
042import org.opencms.main.CmsException;
043import org.opencms.main.CmsLog;
044import org.opencms.main.CmsRuntimeException;
045import org.opencms.main.OpenCms;
046import org.opencms.module.CmsModule;
047import org.opencms.relations.CmsLink;
048import org.opencms.util.CmsFileUtil;
049import org.opencms.util.CmsStringUtil;
050import org.opencms.util.CmsUUID;
051import org.opencms.xml.containerpage.CmsFormatterBean;
052import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
053import org.opencms.xml.containerpage.I_CmsFormatterBean;
054import org.opencms.xml.content.CmsXmlContent;
055import org.opencms.xml.content.CmsXmlContentFactory;
056import org.opencms.xml.content.CmsXmlContentProperty;
057import org.opencms.xml.content.CmsXmlContentProperty.Visibility;
058import org.opencms.xml.content.CmsXmlContentRootLocation;
059import org.opencms.xml.content.I_CmsXmlContentLocation;
060import org.opencms.xml.content.I_CmsXmlContentValueLocation;
061import org.opencms.xml.types.CmsXmlVarLinkValue;
062import org.opencms.xml.types.CmsXmlVfsFileValue;
063
064import java.util.ArrayList;
065import java.util.HashSet;
066import java.util.LinkedHashMap;
067import java.util.LinkedHashSet;
068import java.util.List;
069import java.util.Locale;
070import java.util.Map;
071import java.util.Set;
072
073import org.apache.commons.logging.Log;
074
075import com.google.common.collect.Lists;
076
077/**
078 * A class to parse ADE sitemap or module configuration files and create configuration objects from them.<p>
079 */
080public class CmsConfigurationReader {
081
082    /**
083     * Enum describing how to deal with inherited properties.
084     */
085    enum DiscardPropertiesMode {
086
087        /** Remove properties from parent sitemaps. */
088        discard("true"),
089
090        /** Inherit properties from parent sitemaps. */
091        keep("false"),
092
093        /** Remove properties from parent sitemaps, and mark properties defined in this sitemap as 'top', which moves them over the properties which are only defined in opencms-workplace.xml/opencms-modules.xml. */
094        top("top");
095
096        /** The value representing the mode in an actual sitemap configuration content. */
097        private String m_stringValue;
098
099        /**
100         * Creates the enum value.
101         *
102         * @param stringValue the string value from the configuration
103         */
104        private DiscardPropertiesMode(String stringValue) {
105
106            m_stringValue = stringValue;
107
108        }
109
110        /**
111         * Gets the string value occurring in the  sitemap configuration.
112         *
113         * @return the string value
114         */
115        public String getStringValue() {
116
117            return m_stringValue;
118        }
119    }
120
121    /** The default locale for configuration objects. */
122    public static final Locale DEFAULT_LOCALE = CmsLocaleManager.getLocale("en");
123
124    /** The AddContentReplacements node name. */
125    public static final String N_ADD_CONTENT_RESTRICTION = "AddContentRestriction";
126
127    /** Node name for added formatters. */
128    public static final String N_ADD_FORMATTER = "AddFormatter";
129
130    /** Node name for the nested content with the added formatters. */
131    public static final String N_ADD_FORMATTERS = "AddFormatters";
132
133    /** The AddPlugin node name. */
134    public static final String N_ADD_PLUGIN = "AddPlugin";
135
136    /** The AddPlugins node name. */
137    public static final String N_ADD_PLUGINS = "AddPlugins";
138
139    /** The Attribute node name. */
140    public static final String N_ATTRIBUTE = "Attribute";
141
142    /** Node name for the attribute editor configuration reference. */
143    public static final String N_ATTRIBUTE_EDITOR_CONFIG = "AttributeEditorConfig";
144
145    /** The CopyInModels node name. */
146    public static final String N_COPY_IN_MODELS = "CopyInModels";
147
148    /** The create content locally node name. */
149    public static final String N_CREATE_CONTENTS_LOCALLY = "CreateContentsLocally";
150
151    /** The default node name. */
152    public static final String N_DEFAULT = "Default";
153
154    /** The description node name. */
155    public static final String N_DESCRIPTION = "Description";
156
157    /** The detail page node name. */
158    public static final String N_DETAIL_PAGE = "DetailPage";
159
160    /** The detail pages disabled node name. */
161    public static final String N_DETAIL_PAGES_DISABLED = "DetailPagesDisabled";
162
163    /** The disabled node name. */
164    public static final String N_DISABLED = "Disabled";
165
166    /** The DisabledTypesMode node name. */
167    public static final String N_DISABLED_TYPES_MODE = "DisabledTypesMode";
168
169    /** The DisabledFunctionsMode node name. */
170    public static final String N_DISABLED_FUNCTIONS_MODE = "DisabledFunctionsMode";
171
172    /** The discard model pages node name. */
173    public static final String N_DISCARD_MODEL_PAGES = "DiscardModelPages";
174
175    /** The discard properties node name. */
176    public static final String N_DISCARD_PROPERTIES = "DiscardProperties";
177
178    /** The discard types node name. */
179    public static final String N_DISCARD_TYPES = "DiscardTypes";
180
181    /** The display name node name. */
182    public static final String N_DISPLAY_NAME = "DisplayName";
183
184    /** The element view node name. */
185    public static final String N_ELEMENT_VIEW = "ElementView";
186
187    /** The error node name. */
188    public static final String N_ERROR = "Error";
189
190    /** The 'exclude external detail contents' node name. */
191    public static final String N_EXCLUDE_EXTERNAL_DETAIL_CONTENTS = "ExcludeExternalDetailContents";
192
193    /** The folder node name. */
194    public static final String N_FOLDER = "Folder";
195
196    /** The formatter node name. */
197    public static final String N_FORMATTER = "Formatter";
198
199    /** The function node name. */
200    public static final String N_FUNCTION = "Function";
201
202    /** The function node name. */
203    public static final String N_FUNCTION_DEFAULT_PAGE = "FunctionDefaultPage";
204
205    /** The function reference node name. */
206    public static final String N_FUNCTION_REF = "FunctionRef";
207
208    /** The 'include in site selector' node name. */
209    public static final String N_INCLUDE_IN_SITE_SELECTOR = "IncludeInSiteSelector";
210
211    /** The IncludeName node name. */
212    public static final String N_INCLUDE_NAME = "IncludeName";
213
214    /** The is default node name. */
215    public static final String N_IS_DEFAULT = "IsDefault";
216
217    /** The is preview node name. */
218    public static final String N_IS_PREVIEW = "IsPreview";
219
220    /** The JSP node name. */
221    public static final String N_JSP = "Jsp";
222
223    /** The Key node name. */
224    public static final String N_KEY = "Key";
225
226    /** The localization node name. */
227    public static final String N_LOCALIZATION = "Localization";
228
229    /** The master configuration node name. */
230    public static final String N_MASTER_CONFIG = "MasterConfig";
231
232    /** The max width node name. */
233    public static final String N_MAX_WIDTH = "MaxWidth";
234
235    /** The min width node name. */
236    public static final String N_MIN_WIDTH = "MinWidth";
237
238    /** The model page node name. */
239    public static final String N_MODEL_PAGE = "ModelPage";
240
241    /** The folder name node name. */
242    public static final String N_NAME = "Name";
243
244    /** The name pattern node name. */
245    public static final String N_NAME_PATTERN = "NamePattern";
246
247    /** The order node name. */
248    public static final String N_ORDER = "Order";
249
250    /** The page node name. */
251    public static final String N_PAGE = "Page";
252
253    /** The PageRelative node name. */
254    public static final String N_PAGE_RELATIVE = "PageRelative";
255
256    /** The folder path node name. */
257    public static final String N_PATH = "Path";
258
259    /** The Plugin node name. */
260    public static final String N_PLUGIN = "Plugin";
261
262    /** The  PreferDetailPagesForLocalContents node name. */
263    public static final String N_PREFER_DETAIL_PAGES_FOR_LOCAL_CONTENTS = "PreferDetailPagesForLocalContents";
264
265    /** The prefer folder node name. */
266    public static final String N_PREFER_FOLDER = "PreferFolder";
267
268    /** The property node name. */
269    public static final String N_PROPERTY = "Property";
270
271    /** The property name node name. */
272    public static final String N_PROPERTY_NAME = "PropertyName";
273
274    /** Node name for the "Remove all formatters"-option. */
275    public static final String N_REMOVE_ALL_FORMATTERS = "RemoveAllFormatters";
276
277    /** Field name for the 'Remove all functions' setting. */
278    public static final String N_REMOVE_ALL_FUNCTIONS = "RemoveAllFunctions";
279
280    /** The RemoveAllPlugins node name. */
281    public static final String N_REMOVE_ALL_PLUGINS = "RemoveAllPlugins";
282
283    /** The RemoveAllSharedSettingOverrides node name. */
284    public static final String N_REMOVE_ALL_SHARED_SETTING_OVERRIDES = "RemoveAllSharedSettingOverrides";
285
286    /** Node name for removed formatters. */
287    public static final String N_REMOVE_FORMATTER = "RemoveFormatter";
288
289    /** Node name for the nested content with the removed formatters. */
290    public static final String N_REMOVE_FORMATTERS = "RemoveFormatters";
291
292    /** The remove function node name. */
293    public static final String N_REMOVE_FUNCTIONS = "RemoveFunctions";
294
295    /** The RemovePlugin node name. */
296    public static final String N_REMOVE_PLUGIN = "RemovePlugin";
297
298    /** The RemovePlugins node name. */
299    public static final String N_REMOVE_PLUGINS = "RemovePlugins";
300
301    /** The resource type node name. */
302    public static final String N_RESOURCE_TYPE = "ResourceType";
303
304    /** The regex rule node name. */
305    public static final String N_RULE_REGEX = "RuleRegex";
306
307    /** The rule type node name. */
308    public static final String N_RULE_TYPE = "RuleType";
309
310    /** The SharedSettingOverride node name. */
311    public static final String N_SHARED_SETTING_OVERRIDE = "SharedSettingOverride";
312
313    /** The ShowInDefaultView node name. */
314    public static final String N_SHOW_IN_DEFAULT_VIEW = "ShowInDefaultView";
315
316    /** The type node name. */
317    public static final String N_TYPE = "Type";
318
319    /** The type name node name. */
320    public static final String N_TYPE_NAME = "TypeName";
321
322    /** The node name for the type ordering mode. */
323    public static final String N_TYPE_ORDERING_MODE = "TypeOrderingMode";
324
325    /** Node name. */
326    public static final String N_USE_FORMATTER_KEYS = "UseFormatterKeys";
327
328    /** The Value node name. */
329    public static final String N_VALUE = "Value";
330
331    /** The widget node name. */
332    public static final String N_VISIBILITY = "Visibility";
333
334    /** The widget node name. */
335    public static final String N_WIDGET = "Widget";
336
337    /** The widget configuration node name. */
338    public static final String N_WIDGET_CONFIG = "WidgetConfig";
339
340    /** Scheme for explorer type view links. */
341    public static final String VIEW_SCHEME = "view://";
342
343    /** The log object for this class. */
344    private static final Log LOG = CmsLog.getLog(CmsConfigurationReader.class);
345
346    /** The ElementDeleteMode node name. */
347    private static final String N_ELEMENT_DELETE_MODE = "ElementDeleteMode";
348
349    /** The CMS context used for reading the configuration data. */
350    private CmsObject m_cms;
351
352    /** The parsed detail page configuration elements. */
353    private List<CmsDetailPageInfo> m_detailPageConfigs = new ArrayList<CmsDetailPageInfo>();
354
355    /** The list of configured function references. */
356    private List<CmsFunctionReference> m_functionReferences = new ArrayList<CmsFunctionReference>();
357
358    /** The parsed model page configuration elements. */
359    private List<CmsModelPageConfigWithoutResource> m_modelPageConfigs = new ArrayList<CmsModelPageConfigWithoutResource>();
360
361    /** The parsed property configuration elements. */
362    private List<CmsPropertyConfig> m_propertyConfigs = new ArrayList<CmsPropertyConfig>();
363
364    /** The resource type configuration objects. */
365    private List<CmsResourceTypeConfig> m_resourceTypeConfigs = new ArrayList<CmsResourceTypeConfig>();
366
367    /**
368     * Creates a new configuration reader.<p>
369     *
370     * @param cms the CMS context which should be used to read the configuration data.<p>
371     */
372    public CmsConfigurationReader(CmsObject cms) {
373
374        m_cms = cms;
375    }
376
377    /**
378     * Gets the string value of an XML content location.<p>
379     *
380     * @param cms the CMS context to use
381     * @param location an XML content location
382     *
383     * @return the string value of that XML content location
384     */
385    public static String getString(CmsObject cms, I_CmsXmlContentValueLocation location) {
386
387        if (location == null) {
388            return null;
389        }
390        return location.asString(cms);
391    }
392
393    /**
394     * Helper method to parse a property.<p>
395     *
396     * @param cms the CMS context to use
397     * @param field the location of the parent value
398     *
399     * @return the parsed property configuration
400     */
401    public static CmsPropertyConfig parseProperty(CmsObject cms, I_CmsXmlContentLocation field) {
402
403        String name = getString(cms, field.getSubValue(N_PROPERTY_NAME));
404        String includeName = getString(cms, field.getSubValue(N_INCLUDE_NAME));
405        String widget = getString(cms, field.getSubValue(N_WIDGET));
406        String widgetConfig = getString(cms, field.getSubValue(N_WIDGET_CONFIG));
407        String ruleRegex = getString(cms, field.getSubValue(N_RULE_REGEX));
408        String ruleType = getString(cms, field.getSubValue(N_RULE_TYPE));
409        String default1 = getString(cms, field.getSubValue(N_DEFAULT));
410        String error = getString(cms, field.getSubValue(N_ERROR));
411        String niceName = getString(cms, field.getSubValue(N_DISPLAY_NAME));
412        String description = getString(cms, field.getSubValue(N_DESCRIPTION));
413        String preferFolder = getString(cms, field.getSubValue(N_PREFER_FOLDER));
414
415        String disabledStr = getString(cms, field.getSubValue(N_DISABLED));
416        boolean disabled = ((disabledStr != null) && Boolean.parseBoolean(disabledStr));
417
418        String orderStr = getString(cms, field.getSubValue(N_ORDER));
419        int order = I_CmsConfigurationObject.DEFAULT_ORDER;
420
421        try {
422            order = Integer.parseInt(orderStr);
423        } catch (NumberFormatException e) {
424            // noop
425        }
426
427        Visibility visibility;
428        String visibilityStr = getString(cms, field.getSubValue(N_VISIBILITY));
429        try {
430            // to stay compatible with former visibility option values
431            if ("both".equals(visibilityStr)) {
432                visibilityStr = Visibility.elementAndParentIndividual.name();
433            } else if ("parent".equals(visibilityStr)) {
434                visibilityStr = Visibility.parentShared.name();
435            }
436            visibility = Visibility.valueOf(visibilityStr);
437        } catch (Exception e) {
438            visibility = null;
439        }
440        CmsXmlContentProperty prop = new CmsXmlContentProperty(
441            name,
442            "string",
443            visibility,
444            widget,
445            widgetConfig,
446            ruleRegex,
447            ruleType,
448            default1,
449            niceName,
450            description,
451            error,
452            preferFolder).withIncludeName(includeName);
453        // since these are real properties, using type vfslist makes no sense, so we always use the "string" type
454        CmsPropertyConfig propConfig = new CmsPropertyConfig(prop, disabled, order);
455        return propConfig;
456
457    }
458
459    /**
460     * Returns the list of function references.<p>
461     *
462     * @return the list of function references
463     */
464    public List<CmsFunctionReference> getFunctionReferences() {
465
466        return new ArrayList<CmsFunctionReference>(m_functionReferences);
467    }
468
469    /**
470     * Returns the modelPageConfigs.<p>
471     *
472     * @return the modelPageConfigs
473     */
474    public List<CmsModelPageConfigWithoutResource> getModelPageConfigs() {
475
476        return m_modelPageConfigs;
477    }
478
479    /**
480     * Parses the formatters to add.<p>
481     *
482     * @param node the parent node
483     * @return the set of keys of the formatters to add
484     */
485    public Set<String> parseAddFormatters(I_CmsXmlContentLocation node) {
486
487        Set<String> addFormatters = new HashSet<String>();
488        for (I_CmsXmlContentValueLocation addLoc : node.getSubValues(N_ADD_FORMATTERS + "/" + N_ADD_FORMATTER)) {
489            CmsXmlVfsFileValue value = (CmsXmlVfsFileValue)addLoc.getValue();
490            CmsLink link = value.getLink(m_cms);
491            if (link != null) {
492                CmsUUID structureId = link.getStructureId();
493                if (structureId != null) {
494                    addFormatters.add(structureId.toString());
495                }
496            }
497        }
498        return addFormatters;
499    }
500
501    /**
502     * Parses a configuration XML content and creates a configuration object from it.<p>
503     *
504     * @param basePath the base path
505     * @param content the XML content
506     *
507     * @return the created configuration object with the data from the XML content
508     * @throws CmsException if something goes wrong
509     */
510    public CmsADEConfigDataInternal parseConfiguration(String basePath, CmsXmlContent content) throws CmsException {
511
512        m_detailPageConfigs = Lists.newArrayList();
513        m_functionReferences = Lists.newArrayList();
514        m_modelPageConfigs = Lists.newArrayList();
515        m_propertyConfigs = Lists.newArrayList();
516        m_resourceTypeConfigs = Lists.newArrayList();
517
518        if (!content.hasLocale(DEFAULT_LOCALE)) {
519            return CmsADEConfigDataInternal.emptyConfiguration(basePath);
520        }
521        CmsXmlContentRootLocation root = new CmsXmlContentRootLocation(content, DEFAULT_LOCALE);
522        for (I_CmsXmlContentValueLocation node : root.getSubValues(N_RESOURCE_TYPE)) {
523            try {
524                parseResourceTypeConfig(basePath, node);
525            } catch (CmsException e) {
526                LOG.warn(e.getLocalizedMessage(), e);
527            }
528        }
529        for (I_CmsXmlContentValueLocation node : root.getSubValues(N_MODEL_PAGE)) {
530            try {
531                parseModelPage(node);
532            } catch (Exception e) {
533                LOG.warn(e.getLocalizedMessage(), e);
534            }
535        }
536        for (I_CmsXmlContentLocation node : root.getSubValues(N_DETAIL_PAGE)) {
537            try {
538                parseDetailPage(node);
539            } catch (Exception e) {
540                LOG.warn(e.getLocalizedMessage(), e);
541            }
542        }
543
544        for (I_CmsXmlContentLocation node : root.getSubValues(N_FUNCTION_REF)) {
545            parseFunctionReference(node);
546        }
547
548        CmsUUID sharedSettingOverride = null;
549        for (I_CmsXmlContentValueLocation node : root.getSubValues(N_SHARED_SETTING_OVERRIDE)) {
550            sharedSettingOverride = ((CmsXmlVfsFileValue)node.getValue()).getLink(m_cms).getStructureId();
551        }
552
553        boolean removeSharedSettingOverrides = getBoolean(root, N_REMOVE_ALL_SHARED_SETTING_OVERRIDES);
554
555        boolean removeFunctions = false;
556        removeFunctions = getBoolean(root, N_REMOVE_ALL_FUNCTIONS);
557
558        Set<CmsUUID> functions = new LinkedHashSet<>();
559        for (I_CmsXmlContentValueLocation node : root.getSubValues(N_FUNCTION)) {
560            CmsXmlVfsFileValue value = (CmsXmlVfsFileValue)node.getValue();
561            CmsLink link = value.getLink(m_cms);
562            if (link != null) {
563                CmsUUID structureId = link.getStructureId();
564                if (structureId != null) {
565                    functions.add(link.getStructureId());
566                }
567            }
568        }
569
570        Set<CmsUUID> functionsToRemove = new LinkedHashSet<>();
571        for (I_CmsXmlContentValueLocation parent : root.getSubValues(N_REMOVE_FUNCTIONS)) {
572            for (I_CmsXmlContentValueLocation node : parent.getSubValues(N_FUNCTION)) {
573                CmsXmlVfsFileValue value = (CmsXmlVfsFileValue)node.getValue();
574                CmsLink link = value.getLink(m_cms);
575                if (link != null) {
576                    CmsUUID structureId = link.getStructureId();
577                    if (structureId != null) {
578                        functionsToRemove.add(link.getStructureId());
579                    }
580                }
581            }
582        }
583
584        boolean removeAllPlugins = getBoolean(root, N_REMOVE_ALL_PLUGINS);
585        Set<CmsUUID> pluginsToRemove = readInternalLinkListTargetIds(root, N_REMOVE_PLUGINS, N_PLUGIN);
586        Set<CmsUUID> pluginsToAdd = readInternalLinkListTargetIds(root, N_ADD_PLUGINS, N_PLUGIN);
587
588        boolean removeAllFormatters = getBoolean(root, N_REMOVE_ALL_FORMATTERS);
589        CmsFormatterChangeSet formatterChangeSet = parseFormatterChangeSet(
590            basePath,
591            root,
592            removeAllFormatters,
593            removeFunctions,
594            functions,
595            functionsToRemove);
596        boolean discardInheritedTypes = getBoolean(root, N_DISCARD_TYPES);
597        // boolean discardInheritedProperties = getBoolean(root, N_DISCARD_PROPERTIES);
598        I_CmsXmlContentValueLocation discardPropertiesLoc = root.getSubValue(N_DISCARD_PROPERTIES);
599        DiscardPropertiesMode discardPropertiesMode = DiscardPropertiesMode.keep;
600        if (discardPropertiesLoc != null) {
601            String discardPropertiesStr = discardPropertiesLoc.getValue().getStringValue(m_cms);
602            for (DiscardPropertiesMode discardMode : DiscardPropertiesMode.values()) {
603                if (discardMode.getStringValue().contentEquals(discardPropertiesStr)) {
604                    discardPropertiesMode = discardMode;
605                }
606            }
607        }
608        for (I_CmsXmlContentLocation node : root.getSubValues(N_PROPERTY)) {
609            parseProperty(node, discardPropertiesMode);
610        }
611
612        boolean discardInheritedModelPages = getBoolean(root, N_DISCARD_MODEL_PAGES);
613
614        boolean createContentsLocally = getBoolean(root, N_CREATE_CONTENTS_LOCALLY);
615        boolean preferDetailPagesForLocalContents = getBoolean(root, N_PREFER_DETAIL_PAGES_FOR_LOCAL_CONTENTS);
616        boolean exludeExternalDetailContents = getBoolean(root, N_EXCLUDE_EXTERNAL_DETAIL_CONTENTS);
617        boolean includeInSiteSelector = getBoolean(root, N_INCLUDE_IN_SITE_SELECTOR);
618
619        String galleryDisabledTypesStr = getString(root.getSubValue(N_DISABLED_TYPES_MODE));
620        CmsGalleryDisabledTypesMode galleryDisabledTypesMode = null;
621        if (galleryDisabledTypesStr != null) {
622            galleryDisabledTypesMode = CmsGalleryDisabledTypesMode.valueOf(galleryDisabledTypesStr);
623        }
624
625        String galleryDisabledFunctionsStr = getString(root.getSubValue(N_DISABLED_FUNCTIONS_MODE));
626        CmsGalleryDisabledTypesMode galleryDisabledFunctionsMode = null;
627        if (galleryDisabledFunctionsStr != null) {
628            galleryDisabledFunctionsMode = CmsGalleryDisabledTypesMode.valueOf(galleryDisabledFunctionsStr);
629        }
630
631        I_CmsXmlContentValueLocation typeOrderingLoc = root.getSubValue(N_TYPE_ORDERING_MODE);
632        CmsTypeOrderingMode typeOrderingMode = null;
633        if (typeOrderingLoc != null) {
634            boolean byDisplayOrder = Boolean.parseBoolean(typeOrderingLoc.getValue().getStringValue(m_cms));
635            typeOrderingMode = byDisplayOrder ? CmsTypeOrderingMode.byDisplayOrder : CmsTypeOrderingMode.latestOnTop;
636        }
637
638        I_CmsXmlContentValueLocation useFormatterKeysLoc = root.getSubValue(N_USE_FORMATTER_KEYS);
639        Boolean useFormatterKeys = null;
640        if (useFormatterKeysLoc != null) {
641            useFormatterKeys = Boolean.valueOf(useFormatterKeysLoc.getValue().getStringValue(m_cms));
642        }
643
644        boolean isModuleConfig = OpenCms.getResourceManager().getResourceType(
645            content.getFile().getTypeId()).getTypeName().equals(CmsADEManager.MODULE_CONFIG_TYPE);
646
647        List<CmsUUID> masterConfigIds = new ArrayList<>();
648        for (I_CmsXmlContentValueLocation masterConfigLoc : root.getSubValues(N_MASTER_CONFIG)) {
649            CmsUUID id = masterConfigLoc.asId(m_cms);
650            if (id != null) {
651                masterConfigIds.add(id);
652            }
653        }
654
655        Map<String, String> attributes = new LinkedHashMap<>();
656        for (I_CmsXmlContentValueLocation mappingLoc : root.getSubValues(N_ATTRIBUTE)) {
657            String key = getString(mappingLoc.getSubValue(N_KEY)).trim();
658            String value = getString(mappingLoc.getSubValue(N_VALUE)).trim();
659            attributes.put(key, value);
660        }
661
662        I_CmsXmlContentValueLocation attributeEditorConfigLoc = root.getSubValue(N_ATTRIBUTE_EDITOR_CONFIG);
663        CmsUUID attributeEditorConfigId = null;
664        if (attributeEditorConfigLoc != null) {
665            attributeEditorConfigId = attributeEditorConfigLoc.asId(m_cms);
666        }
667
668        CmsAddContentRestriction addContentRestriction = CmsAddContentRestriction.read(
669            m_cms,
670            root,
671            N_ADD_CONTENT_RESTRICTION);
672
673        CmsADEConfigDataInternal result = new CmsADEConfigDataInternal(
674            m_cms,
675            content.getFile(),
676            isModuleConfig,
677            basePath,
678            masterConfigIds,
679            m_resourceTypeConfigs,
680            galleryDisabledTypesMode,
681            galleryDisabledFunctionsMode,
682            discardInheritedTypes,
683            m_propertyConfigs,
684            discardPropertiesMode,
685            m_detailPageConfigs,
686            m_modelPageConfigs,
687            m_functionReferences,
688            discardInheritedModelPages,
689            createContentsLocally,
690            preferDetailPagesForLocalContents,
691            exludeExternalDetailContents,
692            includeInSiteSelector,
693            formatterChangeSet,
694            removeFunctions,
695            functions,
696            functionsToRemove,
697            removeAllPlugins,
698            pluginsToAdd,
699            pluginsToRemove,
700            useFormatterKeys,
701            typeOrderingMode,
702            addContentRestriction,
703            sharedSettingOverride,
704            removeSharedSettingOverrides,
705            attributeEditorConfigId,
706            attributes);
707        return result;
708    }
709
710    /**
711     * Parses a folder which may either be given as a path or as a folder name.<p>
712     *
713     * @param basePath the  base path for the configuration
714     * @param location the XML content node from which to parse the folder
715     * @return the folder bean
716     *
717     * @throws CmsException if something goes wrong
718     */
719    public CmsContentFolderDescriptor parseFolderOrName(String basePath, I_CmsXmlContentLocation location)
720    throws CmsException {
721
722        if (location == null) {
723            return null;
724        }
725        I_CmsXmlContentValueLocation nameLoc = location.getSubValue(N_NAME);
726        I_CmsXmlContentValueLocation pathLoc = location.getSubValue(N_PATH);
727        I_CmsXmlContentValueLocation pageRelativeLoc = location.getSubValue(N_PAGE_RELATIVE);
728        if (nameLoc != null) {
729            String name = nameLoc.asString(m_cms);
730            return new CmsContentFolderDescriptor(
731                basePath == null ? null : CmsStringUtil.joinPaths(basePath, CmsADEManager.CONTENT_FOLDER_NAME),
732                name);
733        } else if (pathLoc != null) {
734            String path = pathLoc.asString(m_cms);
735            CmsResource folder = m_cms.readResource(path);
736            return new CmsContentFolderDescriptor(folder);
737        } else if (pageRelativeLoc != null) {
738            return CmsContentFolderDescriptor.createPageRelativeFolderDescriptor();
739        } else {
740            return null;
741        }
742    }
743
744    /**
745     * Parses a formatter bean.<p>
746     *
747     * @param typeName the type name for which the formatter is being parsed
748     * @param node the node from which to parse the formatter data
749     *
750     * @return the formatter bean from the XML
751     */
752    public CmsFormatterBean parseFormatter(String typeName, I_CmsXmlContentLocation node) {
753
754        String type = getString(node.getSubValue(N_TYPE));
755        String minWidth = getString(node.getSubValue(N_MIN_WIDTH));
756        String maxWidth = getString(node.getSubValue(N_MAX_WIDTH));
757        boolean preview = false;
758        I_CmsXmlContentValueLocation previewLoc = node.getSubValue(N_IS_PREVIEW);
759        preview = (previewLoc != null) && Boolean.parseBoolean(previewLoc.asString(m_cms));
760        String jsp = m_cms.getRequestContext().addSiteRoot(getString(node.getSubValue(N_JSP)));
761        boolean searchContent = true;
762        CmsFormatterBean formatterBean = new CmsFormatterBean(
763            type,
764            jsp,
765            minWidth,
766            maxWidth,
767            "" + preview,
768            "" + searchContent,
769            null);
770        return formatterBean;
771
772    }
773
774    /**
775     * Parses model page data from the XML content.<p>
776     *
777     * @param node the XML content node
778     */
779    public void parseModelPage(I_CmsXmlContentLocation node) {
780
781        CmsXmlVfsFileValue pageValue = (CmsXmlVfsFileValue)node.getSubValue(N_PAGE).getValue();
782        CmsLink link = pageValue.getUncheckedLink();
783        if ((link == null) || (link.getStructureId() == null)) {
784            return;
785        }
786        I_CmsXmlContentValueLocation disabledLoc = node.getSubValue(N_DISABLED);
787        boolean disabled = (disabledLoc != null) && Boolean.parseBoolean(disabledLoc.asString(m_cms));
788        I_CmsXmlContentValueLocation defaultLoc = node.getSubValue(N_IS_DEFAULT);
789        boolean isDefault = (defaultLoc != null) && Boolean.parseBoolean(defaultLoc.asString(m_cms));
790        CmsModelPageConfigWithoutResource modelPage = new CmsModelPageConfigWithoutResource(
791            link.getStructureId(),
792            isDefault,
793            disabled);
794        m_modelPageConfigs.add(modelPage);
795
796    }
797
798    /**
799     * Parses the set of formatters to remove.<p>
800     *
801     * @param node the parent node
802     * @return the set of formatters to remove
803     */
804    public Set<String> parseRemoveFormatters(I_CmsXmlContentLocation node) {
805
806        Set<String> removeFormatters = new HashSet<String>();
807        for (I_CmsXmlContentValueLocation removeLoc : node.getSubValues(
808            N_REMOVE_FORMATTERS + "/" + N_REMOVE_FORMATTER)) {
809            CmsXmlVfsFileValue value = (CmsXmlVfsFileValue)removeLoc.getValue();
810            CmsLink link = value.getLink(m_cms);
811            if (link != null) {
812                CmsUUID structureId = link.getStructureId();
813                if (structureId != null) {
814                    removeFormatters.add(structureId.toString());
815                }
816            }
817        }
818        return removeFormatters;
819    }
820
821    /**
822     * Parses a resource type configuration element from the XML content.<p>
823     *
824     * @param basePath the base path of the configuration
825     * @param node the XML configuration node
826     * @throws CmsException if something goes wrong
827     */
828    public void parseResourceTypeConfig(String basePath, I_CmsXmlContentLocation node) throws CmsException {
829
830        I_CmsXmlContentValueLocation typeNameLoc = node.getSubValue(N_TYPE_NAME);
831        String typeName = typeNameLoc.asString(m_cms);
832        CmsContentFolderDescriptor folderOrName = parseFolderOrName(basePath, node.getSubValue(N_FOLDER));
833        I_CmsXmlContentValueLocation disabledLoc = node.getSubValue(N_DISABLED);
834        boolean disabled = false;
835        boolean addDisabled = false;
836        boolean createDisabled = false;
837        boolean editDisabled = false;
838        boolean listsOnly = false;
839        String disabledStr = disabledLoc == null ? null : disabledLoc.asString(m_cms);
840        boolean availabilityNotSet = false;
841        if (disabledStr != null) {
842            if ("add".equalsIgnoreCase(disabledStr.trim())) {
843                addDisabled = true;
844            } else if ("create".equalsIgnoreCase(disabledStr.trim())) {
845                createDisabled = true;
846            } else if ("createOrEdit".equalsIgnoreCase(disabledStr.trim())) {
847                createDisabled = true;
848                editDisabled = true;
849            } else if ("listsOnly".equalsIgnoreCase(disabledStr.trim())) {
850                listsOnly = true;
851                addDisabled = true;
852            } else {
853                disabled = Boolean.parseBoolean(disabledStr);
854            }
855        } else {
856            availabilityNotSet = true;
857        }
858
859        I_CmsXmlContentValueLocation namePatternLoc = node.getSubValue(N_NAME_PATTERN);
860        String namePattern = null;
861        if (namePatternLoc != null) {
862            namePattern = namePatternLoc.asString(m_cms);
863        }
864
865        boolean detailPagesDisabled = false;
866        I_CmsXmlContentValueLocation detailDisabledLoc = node.getSubValue(N_DETAIL_PAGES_DISABLED);
867        if (detailDisabledLoc != null) {
868            String detailPagesDisabledStr = detailDisabledLoc.asString(m_cms);
869            detailPagesDisabled = Boolean.parseBoolean(detailPagesDisabledStr);
870        }
871
872        Integer order = null;
873        I_CmsXmlContentValueLocation orderLoc = node.getSubValue(N_ORDER);
874        if (orderLoc != null) {
875            try {
876                String orderStr = orderLoc.asString(m_cms);
877                order = Integer.valueOf(orderStr);
878            } catch (NumberFormatException e) {
879                // noop
880            }
881        }
882
883        I_CmsXmlContentValueLocation elementViewLoc = node.getSubValue(N_ELEMENT_VIEW);
884        CmsUUID elementView = null;
885        if (elementViewLoc != null) {
886            try {
887                CmsXmlVarLinkValue elementViewValue = (CmsXmlVarLinkValue)elementViewLoc.getValue();
888                String stringValue = elementViewValue.getStringValue(m_cms);
889                if ("".equals(stringValue)) {
890                    elementView = CmsUUID.getNullUUID();
891                } else if (stringValue.startsWith(VIEW_SCHEME)) {
892                    elementView = new CmsUUID(stringValue.substring(VIEW_SCHEME.length()));
893                } else {
894                    elementView = elementViewValue.getLink(m_cms).getStructureId();
895                }
896            } catch (Exception e) {
897                // in case parsing the link fails, the default element view will be used
898            }
899        }
900
901        I_CmsXmlContentValueLocation locationLoc = node.getSubValue(N_LOCALIZATION);
902        String localization = null;
903        if (locationLoc != null) {
904            CmsXmlVfsFileValue locationValue = (CmsXmlVfsFileValue)locationLoc.getValue();
905            CmsLink link = locationValue.getLink(m_cms);
906            if (null != link) {
907                String stringValue = link.getSitePath(m_cms);
908                // extract bundle base name from the path to the bundle file
909                int lastSlashIndex = stringValue.lastIndexOf("/");
910                String fileName = stringValue.substring(lastSlashIndex + 1);
911                if (CmsFileUtil.getExtension(fileName).equals(".properties")) {
912                    fileName = fileName.substring(0, fileName.length() - ".properties".length());
913                }
914                String localeSuffix = CmsStringUtil.getLocaleSuffixForName(fileName);
915                if ((localeSuffix != null) && fileName.endsWith(localeSuffix)) {
916                    fileName = fileName.substring(0, fileName.length() - localeSuffix.length() - 1);
917                }
918                localization = fileName;
919            }
920        }
921
922        I_CmsXmlContentValueLocation showDefaultViewLoc = node.getSubValue(N_SHOW_IN_DEFAULT_VIEW);
923        Boolean showInDefaultView = null;
924        if (showDefaultViewLoc != null) {
925            showInDefaultView = Boolean.valueOf(
926                Boolean.parseBoolean(showDefaultViewLoc.getValue().getStringValue(m_cms)));
927        }
928
929        I_CmsXmlContentValueLocation copyInModelsLoc = node.getSubValue(N_COPY_IN_MODELS);
930        Boolean copyInModels = null;
931        if (copyInModelsLoc != null) {
932            copyInModels = Boolean.valueOf(Boolean.parseBoolean(copyInModelsLoc.getValue().getStringValue(m_cms)));
933        }
934
935        I_CmsXmlContentValueLocation elementDeleteModeLoc = node.getSubValue(N_ELEMENT_DELETE_MODE);
936        ElementDeleteMode elementDeleteMode = null;
937        if (elementDeleteModeLoc != null) {
938            try {
939                elementDeleteMode = ElementDeleteMode.valueOf(elementDeleteModeLoc.getValue().getStringValue(m_cms));
940            } catch (Exception e) {
941                LOG.warn(e.getLocalizedMessage(), e);
942            }
943        }
944
945        List<I_CmsFormatterBean> formatters = new ArrayList<I_CmsFormatterBean>();
946        for (I_CmsXmlContentValueLocation formatterLoc : node.getSubValues(N_FORMATTER)) {
947            CmsFormatterBean formatter = parseFormatter(typeName, formatterLoc);
948            formatters.add(formatter);
949        }
950
951        CmsResourceTypeConfig typeConfig = new CmsResourceTypeConfig(
952            typeName,
953            disabled,
954            folderOrName,
955            namePattern,
956            detailPagesDisabled,
957            addDisabled,
958            createDisabled,
959            editDisabled,
960            listsOnly,
961            availabilityNotSet,
962            elementView,
963            localization,
964            showInDefaultView,
965            copyInModels,
966            order,
967            elementDeleteMode);
968        m_resourceTypeConfigs.add(typeConfig);
969    }
970
971    /**
972     * Parses the sitemap configuration given the configuration file and base path.<p>
973     *
974     * @param basePath the base path
975     * @param configRes the configuration file resource
976     * @return the parsed configuration data
977     * @throws CmsException if something goes wrong
978     */
979    public CmsADEConfigDataInternal parseSitemapConfiguration(String basePath, CmsResource configRes)
980    throws CmsException {
981
982        LOG.info("Parsing configuration " + configRes.getRootPath());
983        CmsFile configFile = m_cms.readFile(configRes);
984        CmsXmlContent content = CmsXmlContentFactory.unmarshal(m_cms, configFile);
985        return parseConfiguration(basePath, content);
986    }
987
988    /**
989     * Reads the configurations of all modules and combines them into a single configuration object.<p>
990     *
991     * @return the combined configuration object
992     */
993    public List<CmsADEConfigDataInternal> readModuleConfigurations() {
994
995        List<CmsADEConfigDataInternal> configurations = new ArrayList<CmsADEConfigDataInternal>();
996        List<CmsModule> modules = OpenCms.getModuleManager().getAllInstalledModules();
997        long beginTime = System.currentTimeMillis();
998        for (CmsModule module : modules) {
999            String configPath = module.getConfigurationPath();
1000            if (m_cms.existsResource(configPath)) {
1001                try {
1002                    CmsResource configFile = m_cms.readResource(configPath);
1003                    LOG.info("Found module configuration " + configPath + " for module " + module.getName());
1004                    CmsADEConfigDataInternal config = parseSitemapConfiguration(null, configFile);
1005                    configurations.add(config);
1006                } catch (CmsException e) {
1007                    // errors while parsing configuration
1008                    LOG.error(e.getLocalizedMessage(), e);
1009                } catch (CmsRuntimeException e) {
1010                    // may happen during import of org.opencms.ade.configuration module
1011                    LOG.warn(e.getLocalizedMessage(), e);
1012                } catch (Throwable e) {
1013                    LOG.error(e.getLocalizedMessage(), e);
1014                }
1015            }
1016        }
1017        long endTime = System.currentTimeMillis();
1018        LOG.debug("readModuleConfiguations took " + (endTime - beginTime) + "ms");
1019        return configurations;
1020    }
1021
1022    /**
1023     * Helper method to read a boolean value from the XML.<p>
1024     *
1025     * If the element is not found in the XML, false is returned.<p>
1026     *
1027     * @param parent the parent node
1028     * @param name the name of the XML content value
1029     * @return the boolean value
1030     */
1031    protected boolean getBoolean(I_CmsXmlContentLocation parent, String name) {
1032
1033        I_CmsXmlContentValueLocation location = parent.getSubValue(name);
1034        if (location == null) {
1035            return false;
1036        }
1037        String value = location.getValue().getStringValue(m_cms);
1038        return Boolean.parseBoolean(value);
1039    }
1040
1041    /**
1042     * Gets the string value of an XML content location.<p>
1043     *
1044     * @param location an XML content location
1045     *
1046     * @return the string value of that XML content location
1047     */
1048    protected String getString(I_CmsXmlContentValueLocation location) {
1049
1050        return getString(m_cms, location);
1051    }
1052
1053    /**
1054     * Parses the detail pages from an XML content node.<p>
1055     *
1056     * @param node the XML content node
1057     */
1058    protected void parseDetailPage(I_CmsXmlContentLocation node) {
1059
1060        I_CmsXmlContentValueLocation pageLoc = node.getSubValue(N_PAGE);
1061        String typeName = getString(node.getSubValue(N_TYPE));
1062        CmsXmlVfsFileValue detailPageValue = (CmsXmlVfsFileValue)pageLoc.getValue();
1063        CmsLink uncheckedLink = detailPageValue.getUncheckedLink();
1064        if (uncheckedLink == null) {
1065            LOG.warn(
1066                "Missing detail page link in " + CmsLog.eval(LOG, () -> node.getDocument().getFile().getRootPath()));
1067            return;
1068        }
1069        String page = uncheckedLink.getTarget();
1070        CmsUUID structureId = uncheckedLink.getStructureId();
1071        if (structureId == null) {
1072            return;
1073        }
1074
1075        String iconClasses;
1076        if (typeName.startsWith(CmsDetailPageInfo.FUNCTION_PREFIX)) {
1077            iconClasses = CmsIconUtil.getIconClasses(CmsXmlDynamicFunctionHandler.TYPE_FUNCTION, null, false);
1078        } else {
1079            iconClasses = CmsIconUtil.getIconClasses(typeName, null, false);
1080        }
1081
1082        CmsDetailPageInfo detailPage = new CmsDetailPageInfo(structureId, page, typeName, iconClasses);
1083        m_detailPageConfigs.add(detailPage);
1084
1085    }
1086
1087    /**
1088     * Parses the formatter change set.<p>
1089     *
1090     * @param basePath the configuration base path
1091     * @param node the parent node
1092     * @param removeAllFormatters flag, indicating if all formatters that are not explicitly added should be removed
1093     * @param removeFunctions if true, remove functions
1094     * @param functions the functions to add
1095     * @param functionsToRemove the functions to remove
1096     *
1097     * @return the formatter change set
1098     */
1099    protected CmsFormatterChangeSet parseFormatterChangeSet(
1100        String basePath,
1101        I_CmsXmlContentLocation node,
1102        boolean removeAllFormatters,
1103        boolean removeFunctions,
1104        Set<CmsUUID> functions,
1105        Set<CmsUUID> functionsToRemove) {
1106
1107        Set<String> addFormatters = parseAddFormatters(node);
1108        addFormatters.addAll(readLocalFormatters(node));
1109        Set<String> removeFormatters = removeAllFormatters ? new HashSet<String>() : parseRemoveFormatters(node);
1110        String siteRoot = null;
1111        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(basePath)) {
1112            siteRoot = OpenCms.getSiteManager().getSiteRoot(basePath);
1113        }
1114        CmsFormatterChangeSet result = new CmsFormatterChangeSet(
1115            removeFormatters,
1116            addFormatters,
1117            siteRoot,
1118            removeAllFormatters,
1119            removeFunctions,
1120            functions,
1121            functionsToRemove);
1122        return result;
1123    }
1124
1125    /**
1126     * Parses a function reference node.<p>
1127     *
1128     * @param node the function reference node
1129     */
1130    protected void parseFunctionReference(I_CmsXmlContentLocation node) {
1131
1132        String name = node.getSubValue(N_NAME).asString(m_cms);
1133        CmsUUID functionId = node.getSubValue(N_FUNCTION).asId(m_cms);
1134        CmsUUID functionDefaultPageId = null;
1135        I_CmsXmlContentValueLocation defaultPageValue = node.getSubValue(N_FUNCTION_DEFAULT_PAGE);
1136        if (defaultPageValue != null) {
1137            functionDefaultPageId = defaultPageValue.asId(m_cms);
1138        }
1139        I_CmsXmlContentValueLocation orderNode = node.getSubValue(N_ORDER);
1140        int order = I_CmsConfigurationObject.DEFAULT_ORDER;
1141        if (orderNode != null) {
1142            String orderStr = orderNode.asString(m_cms);
1143            try {
1144                order = Integer.parseInt(orderStr);
1145            } catch (NumberFormatException e) {
1146                // noop
1147            }
1148        }
1149        m_functionReferences.add(new CmsFunctionReference(name, functionId, functionDefaultPageId, order));
1150    }
1151
1152    /**
1153     * Parses a single field definition from a content value.<p>
1154     *
1155     * @param field the content value to parse the field from
1156     * @param mode the property discard mode
1157     */
1158    private void parseProperty(I_CmsXmlContentLocation field, DiscardPropertiesMode mode) {
1159
1160        CmsPropertyConfig propConfig = parseProperty(m_cms, field);
1161        if (mode == DiscardPropertiesMode.top) {
1162            propConfig = propConfig.cloneWithTop(true);
1163        }
1164        m_propertyConfigs.add(propConfig);
1165    }
1166
1167    /**
1168     * Helper method for reading the target ids from a list of internal links two levels nested.
1169     *
1170     * @param root the parent location
1171     * @param childName the node name for the children
1172     * @param grandchildName the node name for the grandchildren
1173     *
1174     * @return the set of target ids collected from the grandchildren
1175     */
1176    private Set<CmsUUID> readInternalLinkListTargetIds(
1177        I_CmsXmlContentLocation root,
1178        String childName,
1179        String grandchildName) {
1180
1181        Set<CmsUUID> result = new LinkedHashSet<>();
1182        for (I_CmsXmlContentValueLocation parent : root.getSubValues(childName)) {
1183            for (I_CmsXmlContentValueLocation node : parent.getSubValues(grandchildName)) {
1184                CmsXmlVfsFileValue value = (CmsXmlVfsFileValue)node.getValue();
1185                CmsLink link = value.getLink(m_cms);
1186                if (link != null) {
1187                    CmsUUID structureId = link.getStructureId();
1188                    if (structureId != null) {
1189                        result.add(link.getStructureId());
1190                    }
1191                }
1192            }
1193        }
1194        return result;
1195    }
1196
1197    /**
1198     * Reads the local macro or flex formatters from the .formatters folder if present.<p>
1199     *
1200     * @param node the xml content node
1201     *
1202     * @return the local formatters
1203     */
1204    private Set<String> readLocalFormatters(I_CmsXmlContentLocation node) {
1205
1206        Set<String> addFormatters = new HashSet<String>();
1207        String path = m_cms.getSitePath(node.getDocument().getFile());
1208        path = CmsStringUtil.joinPaths(CmsResource.getParentFolder(path), ".formatters");
1209        try {
1210            if (m_cms.existsResource(path, CmsResourceFilter.IGNORE_EXPIRATION)) {
1211                I_CmsResourceType macroType = OpenCms.getResourceManager().getResourceType(
1212                    CmsFormatterConfigurationCache.TYPE_MACRO_FORMATTER);
1213                CmsResourceFilter filter = CmsResourceFilter.IGNORE_EXPIRATION.addRequireType(macroType);
1214                List<CmsResource> macroFormatters = m_cms.readResources(path, filter);
1215                for (CmsResource formatter : macroFormatters) {
1216                    addFormatters.add(formatter.getStructureId().toString());
1217                }
1218                I_CmsResourceType flexType = OpenCms.getResourceManager().getResourceType(
1219                    CmsFormatterConfigurationCache.TYPE_FLEX_FORMATTER);
1220                CmsResourceFilter filterFlex = CmsResourceFilter.IGNORE_EXPIRATION.addRequireType(flexType);
1221                List<CmsResource> flexFormatters = m_cms.readResources(path, filterFlex);
1222                for (CmsResource formatter : flexFormatters) {
1223                    addFormatters.add(formatter.getStructureId().toString());
1224                }
1225            }
1226        } catch (CmsException e) {
1227            LOG.warn(e.getMessage(), e);
1228        }
1229        return addFormatters;
1230    }
1231
1232}