001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://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.xml.containerpage;
029
030import org.opencms.ade.configuration.CmsADEConfigData;
031import org.opencms.ade.configuration.CmsADEManager;
032import org.opencms.ade.containerpage.shared.CmsContainerElement;
033import org.opencms.ade.containerpage.shared.CmsContainerElement.ModelGroupState;
034import org.opencms.ade.containerpage.shared.CmsFormatterConfig;
035import org.opencms.ade.containerpage.shared.CmsInheritanceInfo;
036import org.opencms.file.CmsFile;
037import org.opencms.file.CmsObject;
038import org.opencms.file.CmsResource;
039import org.opencms.file.CmsResourceFilter;
040import org.opencms.file.types.CmsResourceTypeXmlContainerPage;
041import org.opencms.file.types.CmsResourceTypeXmlContent;
042import org.opencms.file.types.I_CmsResourceType;
043import org.opencms.main.CmsException;
044import org.opencms.main.OpenCms;
045import org.opencms.util.CmsNullIgnoringConcurrentMap;
046import org.opencms.util.CmsUUID;
047import org.opencms.xml.CmsXmlContentDefinition;
048import org.opencms.xml.content.CmsXmlContent;
049import org.opencms.xml.content.CmsXmlContentFactory;
050import org.opencms.xml.content.CmsXmlContentPropertyHelper;
051
052import java.util.Arrays;
053import java.util.Collections;
054import java.util.HashMap;
055import java.util.Locale;
056import java.util.Map;
057
058import javax.servlet.ServletRequest;
059
060/**
061 * One element of a container in a container page.<p>
062 *
063 * @since 8.0
064 */
065public class CmsContainerElementBean implements Cloneable {
066
067    /** Prevent caching in ADE session cache. */
068    private boolean m_doNotCache;
069
070    /** Flag indicating if a new element should be created replacing the given one on first edit of a container-page. */
071    private final boolean m_createNew;
072
073    /** The client ADE editor hash. */
074    private transient String m_editorHash;
075
076    /** The element's structure id. */
077    private CmsUUID m_elementId;
078
079    /** The formatter's structure id. */
080    private CmsUUID m_formatterId;
081
082    /** The configured properties. */
083    private Map<String, String> m_individualSettings;
084
085    /** The inheritance info of this element. */
086    private CmsInheritanceInfo m_inheritanceInfo;
087
088    /** Indicates whether the represented resource is in memory only and not in the VFS. */
089    private boolean m_inMemoryOnly;
090
091    /** True if the element is used for a historical content. */
092    private boolean m_isHistory;
093
094    /** Indicating if the element resource is released and not expired. */
095    private boolean m_releasedAndNotExpired;
096
097    /** The resource of this element. */
098    private transient CmsResource m_resource;
099
100    /** The settings of this element containing also default values. */
101    private transient Map<String, String> m_settings;
102
103    /** The element site path, only set while rendering. */
104    private String m_sitePath;
105
106    /** Indicates the element bean has a temporary file content set. */
107    private boolean m_temporaryContent;
108
109    /**
110     * Creates a new container page element bean.<p>
111     *
112     * @param file the element's file
113     * @param formatterId the formatter's structure id, could be <code>null</code>
114     * @param individualSettings the element settings as a map of name/value pairs
115     * @param inMemoryOnly the in memory flag
116     * @param editorHash the editor hash to use
117     * @param createNew <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
118     **/
119    public CmsContainerElementBean(
120        CmsFile file,
121        CmsUUID formatterId,
122        Map<String, String> individualSettings,
123        boolean inMemoryOnly,
124        String editorHash,
125        boolean createNew) {
126
127        this(file.getStructureId(), formatterId, individualSettings, createNew);
128        m_inMemoryOnly = inMemoryOnly;
129        m_editorHash = editorHash;
130        m_resource = file;
131    }
132
133    /**
134     * Creates a new container page element bean.<p>
135     *
136     * @param elementId the element's structure id
137     * @param formatterId the formatter's structure id, could be <code>null</code>
138     * @param individualSettings the element settings as a map of name/value pairs
139     * @param createNew <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
140     **/
141    public CmsContainerElementBean(
142        CmsUUID elementId,
143        CmsUUID formatterId,
144        Map<String, String> individualSettings,
145        boolean createNew) {
146
147        m_elementId = elementId;
148        m_formatterId = formatterId;
149        Map<String, String> newSettings = (individualSettings == null
150        ? new HashMap<String, String>()
151        : new HashMap<String, String>(individualSettings));
152        if (!newSettings.containsKey(CmsContainerElement.ELEMENT_INSTANCE_ID)) {
153            newSettings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString());
154        }
155        newSettings.values().removeAll(Collections.singletonList(null));
156        m_individualSettings = Collections.unmodifiableMap(newSettings);
157        m_editorHash = m_elementId.toString() + getSettingsHash();
158        m_createNew = createNew;
159    }
160
161    /**
162     * Constructor to enable wrapped elements.<p>
163     */
164    protected CmsContainerElementBean() {
165
166        m_elementId = null;
167        m_createNew = false;
168    }
169
170    /**
171     * Cloning constructor.<p>
172     *
173     * @param createNew create new flag
174     * @param elementId element id
175     * @param formatterId formatter id
176     * @param individualSettings individual settings
177     * @param inheritanceInfo inheritance info
178     * @param inMemoryOnly in memory only flag
179     * @param temporaryContent temporary content flag
180     * @param releasedAndNotExpired released and not expired flag
181     * @param resource the resource/file object
182     * @param settings the settings
183     * @param sitePath the site path
184     */
185    private CmsContainerElementBean(
186        boolean createNew,
187        CmsUUID elementId,
188        CmsUUID formatterId,
189        Map<String, String> individualSettings,
190        CmsInheritanceInfo inheritanceInfo,
191        boolean inMemoryOnly,
192        boolean temporaryContent,
193        boolean releasedAndNotExpired,
194        CmsResource resource,
195        Map<String, String> settings,
196        String sitePath) {
197
198        m_createNew = createNew;
199        m_elementId = elementId;
200        m_formatterId = formatterId;
201        m_individualSettings = Collections.unmodifiableMap(individualSettings);
202        m_inheritanceInfo = inheritanceInfo;
203        m_inMemoryOnly = inMemoryOnly;
204        m_releasedAndNotExpired = releasedAndNotExpired;
205        m_resource = resource;
206        setSettings(settings);
207        m_sitePath = sitePath;
208        m_temporaryContent = temporaryContent;
209    }
210
211    /**
212     * Clones the given element bean with a different formatter.<p>
213     *
214     * @param source the element to clone
215     * @param formatterId the new formatter id
216     *
217     * @return the element bean
218     */
219    public static CmsContainerElementBean cloneWithFormatter(CmsContainerElementBean source, CmsUUID formatterId) {
220
221        CmsContainerElementBean result = source.clone();
222        result.m_formatterId = formatterId;
223        return result;
224    }
225
226    /**
227     * Clones the given element bean with a different set of settings.<p>
228     *
229     * @param source the element to clone
230     * @param settings the new settings
231     *
232     * @return the element bean
233     */
234    public static CmsContainerElementBean cloneWithSettings(
235        CmsContainerElementBean source,
236        Map<String, String> settings) {
237
238        boolean createNew = source.m_createNew;
239        if (settings.containsKey(CmsContainerElement.CREATE_AS_NEW)) {
240            createNew = Boolean.valueOf(settings.get(CmsContainerElement.CREATE_AS_NEW)).booleanValue();
241            settings = new HashMap<String, String>(settings);
242            settings.remove(CmsContainerElement.CREATE_AS_NEW);
243        }
244        CmsContainerElementBean result = new CmsContainerElementBean(
245            source.m_elementId,
246            source.m_formatterId,
247            settings,
248            createNew);
249        result.m_resource = source.m_resource;
250        result.m_sitePath = source.m_sitePath;
251        result.m_inMemoryOnly = source.m_inMemoryOnly;
252        result.m_inheritanceInfo = source.m_inheritanceInfo;
253        if (result.m_inMemoryOnly) {
254            String editorHash = source.m_editorHash;
255            if (editorHash.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
256                editorHash = editorHash.substring(0, editorHash.indexOf(CmsADEManager.CLIENT_ID_SEPERATOR));
257            }
258            editorHash += result.getSettingsHash();
259            result.m_editorHash = editorHash;
260        }
261        return result;
262    }
263
264    /**
265     * Creates an element bean for the given resource type.<p>
266     * <b>The represented resource will be in memory only and not in the VFS!!!.</b><p>
267     *
268     * @param cms the CMS context
269     * @param resourceType the resource type
270     * @param targetFolder the parent folder of the resource
271     * @param individualSettings the element settings as a map of name/value pairs
272     * @param isCopyModels if this element when used in models should be copied instead of reused
273     * @param locale the locale to use
274     *
275     * @return the created element bean
276     * @throws CmsException if something goes wrong creating the element
277     * @throws IllegalArgumentException if the resource type not instance of {@link org.opencms.file.types.CmsResourceTypeXmlContent}
278     */
279    public static CmsContainerElementBean createElementForResourceType(
280        CmsObject cms,
281        I_CmsResourceType resourceType,
282        String targetFolder,
283        Map<String, String> individualSettings,
284        boolean isCopyModels,
285        Locale locale)
286    throws CmsException {
287
288        if (!(resourceType instanceof CmsResourceTypeXmlContent)) {
289            throw new IllegalArgumentException();
290        }
291
292        byte[] content = new byte[0];
293        String schema = ((CmsResourceTypeXmlContent)resourceType).getSchema();
294        if (schema != null) {
295            // must set URI of OpenCms user context to parent folder of created resource,
296            // in order to allow reading of properties for default values
297            CmsObject newCms = OpenCms.initCmsObject(cms);
298            newCms.getRequestContext().setUri(targetFolder);
299            // unmarshal the content definition for the new resource
300            CmsXmlContentDefinition contentDefinition = CmsXmlContentDefinition.unmarshal(cms, schema);
301            CmsXmlContent xmlContent = CmsXmlContentFactory.createDocument(
302                newCms,
303                locale,
304                OpenCms.getSystemInfo().getDefaultEncoding(),
305                contentDefinition);
306            // adding all other available locales
307            for (Locale otherLocale : OpenCms.getLocaleManager().getAvailableLocales()) {
308                if (!locale.equals(otherLocale)) {
309                    xmlContent.addLocale(newCms, otherLocale);
310                }
311            }
312            content = xmlContent.marshal();
313        }
314        @SuppressWarnings("deprecation")
315        CmsFile file = new CmsFile(
316            CmsUUID.getNullUUID(),
317            CmsUUID.getNullUUID(),
318            targetFolder + "~",
319            resourceType.getTypeId(),
320            0,
321            cms.getRequestContext().getCurrentProject().getUuid(),
322            CmsResource.STATE_NEW,
323            0,
324            cms.getRequestContext().getCurrentUser().getId(),
325            0,
326            cms.getRequestContext().getCurrentUser().getId(),
327            CmsResource.DATE_RELEASED_DEFAULT,
328            CmsResource.DATE_EXPIRED_DEFAULT,
329            1,
330            content.length,
331            0,
332            0,
333            content);
334        CmsContainerElementBean elementBean = new CmsContainerElementBean(
335            file,
336            null,
337            individualSettings,
338            true,
339            resourceType.getTypeName() + getSettingsHash(individualSettings, isCopyModels),
340            isCopyModels);
341        return elementBean;
342    }
343
344    /**
345     * Gets the hash code for the element settings.<p>
346     *
347     * @param individualSettings the individual settings
348     * @param createNew the create new flag
349     *
350     * @return the hash code for the element settings
351     */
352    private static String getSettingsHash(Map<String, String> individualSettings, boolean createNew) {
353
354        if (!individualSettings.isEmpty() || createNew) {
355            int hash = (individualSettings.toString() + createNew).hashCode();
356            return CmsADEManager.CLIENT_ID_SEPERATOR + hash;
357        }
358        return "";
359    }
360
361    /**
362     * Adds a formatter setting.<p>
363     *
364     * @param containerName the container name
365     * @param formatterId the formatter id
366     */
367    public void addFormatterSetting(String containerName, String formatterId) {
368
369        Map<String, String> newSettings = new HashMap<String, String>(m_individualSettings);
370        newSettings.put(CmsFormatterConfig.getSettingsKeyForContainer(containerName), formatterId);
371        m_individualSettings = Collections.unmodifiableMap(newSettings);
372        if (m_inMemoryOnly) {
373            String editorHash = m_editorHash;
374            if (editorHash.contains(CmsADEManager.CLIENT_ID_SEPERATOR)) {
375                editorHash = editorHash.substring(0, editorHash.indexOf(CmsADEManager.CLIENT_ID_SEPERATOR));
376            }
377            editorHash += getSettingsHash();
378            m_editorHash = editorHash;
379        } else {
380            m_editorHash = m_elementId.toString() + getSettingsHash();
381        }
382    }
383
384    /**
385     * @see java.lang.Object#clone()
386     */
387    @Override
388    public CmsContainerElementBean clone() {
389
390        return new CmsContainerElementBean(
391            m_createNew,
392            m_elementId,
393            m_formatterId,
394            m_individualSettings == null ? null : new HashMap<>(m_individualSettings),
395            m_inheritanceInfo,
396            m_inMemoryOnly,
397            m_temporaryContent,
398            m_releasedAndNotExpired,
399            m_resource,
400            m_settings == null ? null : new HashMap<>(m_settings),
401            m_sitePath);
402    }
403
404    /**
405     * Returns the ADE client editor has value.<p>
406     *
407     * @return the ADE client editor has value
408     */
409    public String editorHash() {
410
411        if (m_editorHash == null) {
412            m_editorHash = m_elementId.toString() + getSettingsHash();
413        }
414        return m_editorHash;
415    }
416
417    /**
418     * Ensures the element has a new element instance id.<p>
419     */
420    public void ensureNewInstanceId() {
421
422        Map<String, String> newSettings = new HashMap<String, String>(m_individualSettings);
423        newSettings.put(CmsContainerElement.ELEMENT_INSTANCE_ID, new CmsUUID().toString());
424        m_individualSettings = Collections.unmodifiableMap(newSettings);
425        m_editorHash = m_elementId.toString() + getSettingsHash();
426    }
427
428    /**
429     * @see java.lang.Object#equals(java.lang.Object)
430     */
431    @Override
432    public boolean equals(Object obj) {
433
434        if (!(obj instanceof CmsContainerElementBean)) {
435            return false;
436        }
437        return editorHash().equals(((CmsContainerElementBean)obj).editorHash());
438    }
439
440    /**
441     * Returns the structure id of the formatter of this element.<p>
442     *
443     * @return the structure id of the formatter of this element
444     */
445    public CmsUUID getFormatterId() {
446
447        return m_formatterId;
448    }
449
450    /**
451     * Helper method for getting a formatter key from the settings, for a particular container name.
452     *
453     * <p>First tries the setting formatterSettings#ContainerName, then formatterSettings# as a fallback.
454     * The second case can occur when dealing with elements from the user's favorite/recent list.
455     *
456     * @param containerName the container name
457     * @return the formatter key, or null
458     */
459    public String getFormatterKey(String containerName) {
460
461        if (getIndividualSettings() == null) {
462            return null;
463        }
464        for (String name : Arrays.asList(containerName, "")) {
465            String key = CmsFormatterConfig.getSettingsKeyForContainer(name);
466            String formatter = getIndividualSettings().get(key);
467            if (formatter != null) {
468                return formatter;
469            }
470        }
471        return null;
472    }
473
474    /**
475     * Returns the structure id of the resource of this element.<p>
476     *
477     * @return the structure id of the resource of this element
478     */
479    public CmsUUID getId() {
480
481        return m_elementId;
482    }
483
484    /**
485     * Returns the settings of this element.<p>
486     *
487     * @return the settings of this element
488     */
489    public Map<String, String> getIndividualSettings() {
490
491        return m_individualSettings;
492    }
493
494    /**
495     * Returns the inheritance info.<p>
496     *
497     * @return the inheritance info or <code>null</code> if not available
498     */
499    public CmsInheritanceInfo getInheritanceInfo() {
500
501        return m_inheritanceInfo;
502    }
503
504    /**
505     * Returns the element instance id.<p>
506     *
507     * @return the element instance id
508     */
509    public String getInstanceId() {
510
511        return getIndividualSettings().get(CmsContainerElement.ELEMENT_INSTANCE_ID);
512    }
513
514    /**
515     * Returns the resource of this element.<p>
516     *
517     * It is required to call {@link #initResource(CmsObject)} before this method can be used.<p>
518     *
519     * @return the resource of this element
520     *
521     * @see #initResource(CmsObject)
522     */
523    public CmsResource getResource() {
524
525        return m_resource;
526    }
527
528    /**
529     * Returns the element settings including default values for settings not set.<p>
530     * Will return <code>null</code> if the element bean has not been initialized with {@link #initResource(org.opencms.file.CmsObject)}.<p>
531     *
532     * @return the element settings
533     */
534    public Map<String, String> getSettings() {
535
536        return m_settings;
537    }
538
539    /**
540     * Returns the site path of the resource of this element.<p>
541     *
542     * It is required to call {@link #initResource(CmsObject)} before this method can be used.<p>
543     *
544     * @return the site path of the resource of this element
545     *
546     * @see #initResource(CmsObject)
547     */
548    public String getSitePath() {
549
550        return m_sitePath;
551    }
552
553    /**
554     * Returns the resource type name.<p>
555     *
556     * @return the type name
557     */
558    public String getTypeName() {
559
560        if (getResource() != null) {
561            return OpenCms.getResourceManager().getResourceType(getResource()).getTypeName();
562        } else {
563            return "unknown";
564        }
565    }
566
567    /**
568     * @see java.lang.Object#hashCode()
569     */
570    @Override
571    public int hashCode() {
572
573        return m_editorHash.hashCode();
574    }
575
576    /**
577     * Initializes the resource and the site path of this element.<p>
578     *
579     * @param cms the CMS context
580     *
581     * @throws CmsException if something goes wrong reading the element resource
582     */
583    public void initResource(CmsObject cms) throws CmsException {
584
585        if (m_resource == null) {
586            m_resource = cms.readResource(getId(), CmsResourceFilter.IGNORE_EXPIRATION);
587            m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
588        } else if (!isInMemoryOnly()) {
589            CmsUUID id = m_resource.getStructureId();
590            if (id == null) {
591                id = getId();
592            }
593            // the resource object may have a wrong root path, e.g. if it was created before the resource was moved
594            if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
595                m_resource = cms.readResource(id, CmsResourceFilter.IGNORE_EXPIRATION);
596                m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
597            } else {
598                if (!isTemporaryContent()) {
599                    m_resource = cms.readResource(getId(), CmsResourceFilter.IGNORE_EXPIRATION);
600                }
601                m_releasedAndNotExpired = m_resource.isReleasedAndNotExpired(cms.getRequestContext().getRequestTime());
602            }
603        }
604        if (m_settings == null) {
605            setSettings(new HashMap<String, String>(getIndividualSettings()));
606        }
607        // redo on every init call to ensure sitepath is calculated for current site
608        m_sitePath = cms.getSitePath(m_resource);
609    }
610
611    /**
612     * Initializes the element settings.<p>
613     *
614     * @param cms the CMS context
615     * @param formatterBean the formatter configuration bean
616     * @param locale the content locale
617     * @param request the current request, if available
618     * @param presets the presets for container element settings
619     */
620    public void initSettings(
621        CmsObject cms,
622        CmsADEConfigData config,
623        I_CmsFormatterBean formatterBean,
624        Locale locale,
625        ServletRequest request,
626        Map<String, String> presets) {
627
628        Map<String, String> mergedSettings;
629        if (formatterBean == null) {
630            mergedSettings = CmsXmlContentPropertyHelper.mergeDefaults(
631                cms,
632                config,
633                m_resource,
634                getIndividualSettings(),
635                locale,
636                request);
637        } else {
638            mergedSettings = CmsXmlContentPropertyHelper.mergeDefaults(
639                cms,
640                OpenCms.getADEManager().getFormatterSettings(
641                    cms,
642                    config,
643                    formatterBean,
644                    getResource(),
645                    locale,
646                    request),
647                getIndividualSettings());
648        }
649        if ((presets != null) && (presets.size() > 0)) {
650            mergedSettings.putAll(presets);
651        }
652        if (m_settings == null) {
653            setSettings(mergedSettings);
654        } else {
655            m_settings.putAll(mergedSettings);
656        }
657    }
658
659    /**
660     * Returns if the given element should be used as a copy model.<p>
661     *
662     * @return <code>true</code> if the given element should be used as a copy model
663     */
664    public boolean isCopyModel() {
665
666        return Boolean.valueOf(getIndividualSettings().get(CmsContainerElement.USE_AS_COPY_MODEL)).booleanValue();
667    }
668
669    /**
670     * Returns if a new element should be created replacing the given one on first edit of a container-page.<p>
671     *
672     * @return <code>true</code> if a new element should be created replacing the given one on first edit of a container-page
673     */
674    public boolean isCreateNew() {
675
676        return m_createNew;
677    }
678
679    /**
680     * Checks if the element is uncacheable.
681     *
682     * @return true if the element is uncacheable
683     */
684    public boolean isDoNotCache() {
685
686        return m_doNotCache;
687    }
688
689    /**
690     * Tests whether this element refers to a group container.<p>
691     *
692     * @param cms the CmsObject used for VFS operations
693     *
694     * @return <code>true</code> if the container element refers to a group container
695     *
696     * @throws CmsException if something goes wrong
697     */
698    public boolean isGroupContainer(CmsObject cms) throws CmsException {
699
700        if (m_resource == null) {
701            initResource(cms);
702        }
703        return CmsResourceTypeXmlContainerPage.GROUP_CONTAINER_TYPE_NAME.equals(
704            OpenCms.getResourceManager().getResourceType(m_resource).getTypeName());
705    }
706
707    /**
708     * Checks if the element is used for displaying a historical content (usually from the history dialog).
709     *
710     * @return true if the element is used for displaying a historical content
711     */
712    public boolean isHistoryContent() {
713
714        return m_isHistory;
715    }
716
717    /**
718     * Returns whether this element refers to an inherited container element.<p>
719     *
720     * @param cms the CmsObject used for VFS operations
721     *
722     * @return <code>true</code> if the container element refers to an inherited container
723     *
724     * @throws CmsException if something goes wrong
725     */
726    @SuppressWarnings("deprecation")
727    public boolean isInheritedContainer(CmsObject cms) throws CmsException {
728
729        if (m_resource == null) {
730            initResource(cms);
731        }
732        return OpenCms.getResourceManager().getResourceType(
733            CmsResourceTypeXmlContainerPage.INHERIT_CONTAINER_TYPE_NAME).getTypeId() == m_resource.getTypeId();
734    }
735
736    /**
737     * Returns if the represented resource is in memory only and not persisted in the VFS.<p>
738     *
739     * @return <code>true</code> if the represented resource is in memory only and not persisted in the VFS
740     */
741    public boolean isInMemoryOnly() {
742
743        return m_inMemoryOnly;
744    }
745
746    /**
747     * Returns if the given element is a model group.<p>
748     *
749     * @return <code>true</code> if the given element is a model group
750     */
751    public boolean isModelGroup() {
752
753        ModelGroupState state = ModelGroupState.evaluate(
754            getIndividualSettings().get(CmsContainerElement.MODEL_GROUP_STATE));
755        return state == ModelGroupState.isModelGroup;
756    }
757
758    /**
759     * Returns if all instances of this element should be replaced within a copy model.<p>
760     *
761     * @return <code>true</code> if all instances of this element should be replaced within a copy model
762     */
763    public boolean isModelGroupAlwaysReplace() {
764
765        return Boolean.parseBoolean(getIndividualSettings().get(CmsContainerElement.IS_MODEL_GROUP_ALWAYS_REPLACE));
766    }
767
768    /**
769     * Returns if the element resource is released and not expired.<p>
770     *
771     * @return <code>true</code> if the element resource is released and not expired
772     */
773    public boolean isReleasedAndNotExpired() {
774
775        return isInMemoryOnly() || m_releasedAndNotExpired;
776    }
777
778    /**
779     * Returns if the element resource contains temporary file content.<p>
780     *
781     * @return <code>true</code> if the element resource contains temporary file content
782     */
783    public boolean isTemporaryContent() {
784
785        return m_temporaryContent;
786    }
787
788    /**
789     * Enables / disables 'do not cache' status, which prevents the element from being cached in the session cache.
790     *
791     * @param doNotCache the new value
792     */
793    public void setDoNotCache(boolean doNotCache) {
794
795        m_doNotCache = doNotCache;
796    }
797
798    /**
799     * Sets the formatter id.<p>
800     *
801     * @param formatterId the formatter id
802     */
803    public void setFormatterId(CmsUUID formatterId) {
804
805        m_formatterId = formatterId;
806    }
807
808    /**
809     * Sets a historical file.<p>
810     *
811     * @param file the historical file
812     */
813    public void setHistoryFile(CmsFile file) {
814
815        m_resource = file;
816        m_inMemoryOnly = true;
817        m_isHistory = true;
818    }
819
820    /**
821     * Sets the inheritance info for this element.<p>
822     *
823     * @param inheritanceInfo the inheritance info
824     */
825    public void setInheritanceInfo(CmsInheritanceInfo inheritanceInfo) {
826
827        m_inheritanceInfo = inheritanceInfo;
828    }
829
830    /**
831     * Sets the element resource as a temporary file.<p>
832     *
833     * @param elementFile the temporary file
834     */
835    public void setTemporaryFile(CmsFile elementFile) {
836
837        m_resource = elementFile;
838        m_temporaryContent = true;
839    }
840
841    /**
842     * @see java.lang.Object#toString()
843     */
844    @Override
845    public String toString() {
846
847        return editorHash();
848    }
849
850    /**
851     * Updates the individual settings.<p>
852     *
853     * This causes all merged settings (from defaults etc.) to be lost.
854     *
855     * @param newSettings the new settings
856     */
857    public void updateIndividualSettings(Map<String, String> newSettings) {
858
859        m_individualSettings = Collections.unmodifiableMap(newSettings);
860        setSettings(getIndividualSettings());
861    }
862
863    /**
864     * Gets the hash code for the element settings.<p>
865     *
866     * @return the hash code for the element settings
867     */
868    private String getSettingsHash() {
869
870        String instanceId = getInstanceId();
871        if (instanceId == null) {
872            throw new RuntimeException("Missing instance id");
873        }
874        return CmsADEManager.CLIENT_ID_SEPERATOR
875            + getInstanceId()
876            + "_"
877            + getIndividualSettings().get(CmsContainerElement.SETTING_PAGE_ID);
878    }
879
880    /**
881     * Sets the settings map.<p>
882     *
883     * @param settings the settings
884     */
885    private void setSettings(Map<String, String> settings) {
886
887        if (settings == null) {
888            m_settings = null;
889        } else {
890            m_settings = new CmsNullIgnoringConcurrentMap<String, String>(settings);
891        }
892    }
893}