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.containerpage.shared.CmsCntPageData.ElementDeleteMode;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsProperty;
033import org.opencms.file.CmsRequestContext;
034import org.opencms.file.CmsResource;
035import org.opencms.file.CmsResourceFilter;
036import org.opencms.file.CmsVfsResourceNotFoundException;
037import org.opencms.file.types.CmsResourceTypeFunctionConfig;
038import org.opencms.file.types.I_CmsResourceType;
039import org.opencms.lock.CmsLock;
040import org.opencms.lock.CmsLockException;
041import org.opencms.main.CmsException;
042import org.opencms.main.CmsLog;
043import org.opencms.main.OpenCms;
044import org.opencms.relations.CmsCategoryService;
045import org.opencms.security.CmsPermissionSet;
046import org.opencms.security.CmsRole;
047import org.opencms.ui.util.CmsNewResourceBuilder;
048import org.opencms.util.CmsStringUtil;
049import org.opencms.util.CmsUUID;
050import org.opencms.util.CmsVfsUtil;
051import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
052import org.opencms.xml.containerpage.CmsXmlDynamicFunctionHandler;
053
054import java.util.ArrayList;
055import java.util.HashSet;
056import java.util.Locale;
057import java.util.Set;
058
059import org.apache.commons.logging.Log;
060
061/**
062 * The configuration for a single resource type.<p>
063 */
064public class CmsResourceTypeConfig implements I_CmsConfigurationObject<CmsResourceTypeConfig>, Cloneable {
065
066    /**
067     * Enum used to distinguish the type of menu in which a configured resource type can be displayed.
068     */
069    public enum AddMenuType {
070        /** ADE add menu. */
071        ade,
072
073        /** Workplace dialogs. */
074        workplace
075    }
076
077    /**
078     * Represents the visibility status of a resource type  in  the 'Add' menu of the container page editor.<p>
079     */
080    public enum AddMenuVisibility {
081
082        /** Type should not be creatable. */
083        createDisabled,
084
085        /** Type not visible. */
086        disabled,
087
088        /** Type does not belong to current view, but has been configured to be still visible in it. */
089        fromOtherView,
090
091        /** Type is normally visible. */
092        visible
093    }
094
095    /** The log instance for this class. */
096    private static final Log LOG = CmsLog.getLog(CmsResourceTypeConfig.class);
097
098    /** The CMS object used for VFS operations. */
099    protected CmsObject m_cms;
100
101    /** Flag which controls whether adding elements of this type using ADE is disabled. */
102    private boolean m_addDisabled;
103
104    /** True if availability has not been set in the configuration file.*/
105    private boolean m_availabilityNotSet;
106
107    /** Elements of this type when used in models should be copied instead of reused. */
108    private Boolean m_copyInModels;
109
110    /** Flag which controls whether creating elements of this type using ADE is disabled. */
111    private boolean m_createDisabled;
112
113    /** The flag for disabling detail pages. */
114    private boolean m_detailPagesDisabled;
115
116    /** True if this is a disabled configuration. */
117    private boolean m_disabled;
118
119    /** True if editing is disabled for container elements of this type. */
120    private boolean m_editDisabled;
121
122    /** The element delete mode. */
123    private ElementDeleteMode m_elementDeleteMode;
124
125    /** The element view id. */
126    private CmsUUID m_elementView;
127
128    /** True if this creating/editing for this type should be enabled in lists (e.g. search or contentload tags). */
129    private boolean m_enableInLists;
130
131    /** A reference to a folder of folder name. */
132    private CmsContentFolderDescriptor m_folderOrName;
133
134    /** The bundle to add as workplace bundle for the resource type. */
135    private String m_localization;
136
137    /** The name pattern .*/
138    private String m_namePattern;
139
140    /** The number used for sorting the resource type configurations. */
141    private Integer m_order;
142
143    /** Flag which controls whether this type should be shown in the 'add' menu in the default view. */
144    private Boolean m_showInDefaultView;
145
146    /** The set of template context keys associated with this type via the template=... parameter in master configuration links. */
147    private Set<String> m_templates = new HashSet<>();
148
149    /** The name of the resource type. */
150    private String m_typeName;
151
152    /**
153     * Creates a new resource type configuration.<p>
154     *
155     * @param typeName the resource type name
156     * @param disabled true if this is a disabled configuration
157     * @param folder the folder reference
158     * @param pattern the name pattern
159     */
160    public CmsResourceTypeConfig(String typeName, boolean disabled, CmsContentFolderDescriptor folder, String pattern) {
161
162        this(
163            typeName,
164            disabled,
165            folder,
166            pattern,
167            false,
168            false,
169            false,
170            false,
171            false,
172            false,
173            CmsElementView.DEFAULT_ELEMENT_VIEW.getId(),
174            null,
175            null,
176            null,
177            Integer.valueOf(I_CmsConfigurationObject.DEFAULT_ORDER),
178            null);
179    }
180
181    /**
182     * Creates a new resource type configuration.<p>
183     *
184     * @param typeName the resource type name
185     * @param disabled true if this is a disabled configuration
186     * @param folder the folder reference
187     * @param pattern the name pattern
188     * @param detailPagesDisabled true if detail page creation should be disabled for this type
189     * @param addDisabled true if adding elements of this type via ADE should be disabled
190     * @param editDisabled true if editing container elements of the type should be disabled
191     * @param enableInLists true if the type should be enabled, but only for the direct edit buttons in lists and not ADE/drag/drop.
192     * @param createDisabled true if creating elements of this type via ADE should be disabled
193     * @param availabilityNotSet true if the availability has not been set
194     * @param elementView the element view id
195     * @param localization the base name of the bundle to add as workplace bundle for the resource type
196     * @param showInDefaultView if true, the element type should be shown in the default element view even if it doesn't belong to it
197     * @param copyInModels if elements of this type when used in models should be copied instead of reused
198     * @param order the display order
199     * @param elementDeleteMode the element delete mode
200     *
201     */
202    public CmsResourceTypeConfig(
203        String typeName,
204        boolean disabled,
205        CmsContentFolderDescriptor folder,
206        String pattern,
207        boolean detailPagesDisabled,
208        boolean addDisabled,
209        boolean createDisabled,
210        boolean editDisabled,
211        boolean enableInLists,
212        boolean availabilityNotSet,
213        CmsUUID elementView,
214        String localization,
215        Boolean showInDefaultView,
216        Boolean copyInModels,
217        Integer order,
218        ElementDeleteMode elementDeleteMode) {
219
220        m_typeName = typeName;
221        m_disabled = disabled;
222        m_folderOrName = folder;
223        m_namePattern = pattern;
224        m_detailPagesDisabled = detailPagesDisabled;
225        m_addDisabled = addDisabled;
226        m_createDisabled = createDisabled;
227        m_availabilityNotSet = availabilityNotSet;
228        m_elementView = elementView;
229        m_editDisabled = editDisabled;
230        m_enableInLists = enableInLists;
231        m_localization = localization;
232        m_showInDefaultView = showInDefaultView;
233        m_copyInModels = copyInModels;
234        m_order = order;
235        m_elementDeleteMode = elementDeleteMode;
236    }
237
238    /**
239     * Checks if this resource type is creatable.<p>
240     *
241     * @param cms the current CMS context
242     * @param pageFolderRootPath the root path of the folder containing the current container page
243     *
244     * @return <code>true</code> if the resource type is creatable
245     *
246     * @throws CmsException if something goes wrong
247     */
248    public boolean checkCreatable(CmsObject cms, String pageFolderRootPath) throws CmsException {
249
250        if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
251            return false;
252        }
253        if (OpenCms.getRoleManager().hasRole(cms, CmsRole.ROOT_ADMIN)) {
254            return true;
255        }
256        if (CmsXmlDynamicFunctionHandler.TYPE_FUNCTION.equals(m_typeName)
257            || CmsResourceTypeFunctionConfig.TYPE_NAME.equals(m_typeName)) {
258            return OpenCms.getRoleManager().hasRole(cms, CmsRole.DEVELOPER);
259        }
260        checkInitialized();
261        if ((m_folderOrName != null) && m_folderOrName.isPageRelative() && (pageFolderRootPath == null)) {
262            LOG.info(
263                "type "
264                    + m_typeName
265                    + " not creatable for pageFolderRootPath=null because it is configured to be page-relative");
266            return false;
267        }
268        String folderPath = getFolderPath(cms, pageFolderRootPath);
269        String oldSiteRoot = cms.getRequestContext().getSiteRoot();
270        cms.getRequestContext().setSiteRoot("");
271        //tryToUnlock(cms, folderPath);
272        CmsResource permissionCheckFolder = null;
273        for (String currentPath = folderPath; currentPath != null; currentPath = CmsResource.getParentFolder(
274            currentPath)) {
275            try {
276                permissionCheckFolder = cms.readResource(currentPath);
277                break;
278            } catch (CmsVfsResourceNotFoundException e) {
279                // ignore
280            }
281        }
282        try {
283            if (permissionCheckFolder == null) {
284                return false;
285            }
286            LOG.info("Using " + permissionCheckFolder + " as a permission check folder for " + folderPath);
287            CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_typeName);
288            if (settings == null) {
289                return false;
290            }
291            boolean editable = settings.isEditable(cms, permissionCheckFolder);
292            boolean controlPermission = settings.getAccess().getPermissions(
293                cms,
294                permissionCheckFolder).requiresControlPermission();
295            boolean hasWritePermission = cms.hasPermissions(
296                permissionCheckFolder,
297                CmsPermissionSet.ACCESS_WRITE,
298                false,
299                CmsResourceFilter.ONLY_VISIBLE_NO_DELETED);
300            return editable && controlPermission && hasWritePermission;
301        } catch (CmsVfsResourceNotFoundException e) {
302            return false;
303        } catch (CmsException e) {
304            LOG.error(e.getLocalizedMessage(), e);
305            return false;
306        } finally {
307            cms.getRequestContext().setSiteRoot(oldSiteRoot);
308        }
309    }
310
311    /**
312     * Checks whether the object is initialized and throws an exception otherwise.<p>
313    */
314    public void checkInitialized() {
315
316        if (m_cms == null) {
317            throw new IllegalStateException();
318        }
319    }
320
321    /**
322     * Checks whether the cms context is in the offline project and throws an exception otherwise.<p>
323     *
324     * @param cms the cms context
325     */
326    public void checkOffline(CmsObject cms) {
327
328        if (cms.getRequestContext().getCurrentProject().isOnlineProject()) {
329            throw new IllegalStateException();
330        }
331    }
332
333    /**
334     * Checks if a resource type is viewable for the current user.
335     * If not, this resource type should not be available at all within the ADE 'add-wizard'.<p>
336     *
337     * @param cms the current CMS context
338     * @param referenceUri the resource URI to check permissions for
339     *
340     * @return <code>true</code> if the resource type is viewable
341     */
342    public boolean checkViewable(CmsObject cms, String referenceUri) {
343
344        try {
345            CmsExplorerTypeSettings settings = OpenCms.getWorkplaceManager().getExplorerTypeSetting(m_typeName);
346            CmsResource referenceResource = cms.readResource(
347                referenceUri,
348                CmsResourceFilter.ignoreExpirationOffline(cms));
349            if (settings == null) {
350                // no explorer type
351                return false;
352            }
353            return settings.getAccess().getPermissions(cms, referenceResource).requiresViewPermission();
354        } catch (CmsException e) {
355            LOG.error(e.getLocalizedMessage(), e);
356            return false;
357        }
358    }
359
360    /**
361     * Similar to createNewElement, but just sets parameters on a resource builder instead of actually creating the resource.<p>
362     *
363     * @param cms the CMS context
364     * @param pageFolderRootPath the page folder root path
365     * @param builder the resource builder
366     *
367     * @throws CmsException if something goes wrong
368     */
369    public void configureCreateNewElement(CmsObject cms, String pageFolderRootPath, CmsNewResourceBuilder builder)
370    throws CmsException {
371
372        checkOffline(cms);
373        checkInitialized();
374        String folderPath = getFolderPath(cms, pageFolderRootPath);
375        CmsVfsUtil.createFolder(cms, folderPath);
376        String destination = CmsStringUtil.joinPaths(folderPath, getNamePattern(true));
377        builder.setSiteRoot("");
378        builder.setPatternPath(destination);
379        builder.setType(getTypeName());
380        builder.setLocale(cms.getRequestContext().getLocale());
381    }
382
383    /**
384     * Creates a new element.<p>
385     *
386     * @param userCms the CMS context to use
387     * @param modelResource the model resource to use
388     * @param pageFolderRootPath the root path of the folder containing the current container page
389     *
390     * @return the created resource
391     *
392     * @throws CmsException if something goes wrong
393     */
394    public CmsResource createNewElement(CmsObject userCms, CmsResource modelResource, String pageFolderRootPath)
395    throws CmsException {
396
397        checkOffline(userCms);
398        checkInitialized();
399        CmsObject rootCms = rootCms(userCms);
400        String folderPath = getFolderPath(userCms, pageFolderRootPath);
401        CmsVfsUtil.createFolder(userCms, folderPath);
402        String destination = CmsStringUtil.joinPaths(folderPath, getNamePattern(true));
403        String creationPath = OpenCms.getResourceManager().getNameGenerator().getNewFileName(rootCms, destination, 5);
404        // set the content locale
405        Locale contentLocale = userCms.getRequestContext().getLocale();
406        if (!OpenCms.getLocaleManager().getAvailableLocales(rootCms, folderPath).contains(contentLocale)) {
407            contentLocale = OpenCms.getLocaleManager().getDefaultLocale(rootCms, folderPath);
408        }
409        rootCms.getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_NEW_RESOURCE_LOCALE, contentLocale);
410        if (modelResource != null) {
411            // set the model resource
412            rootCms.getRequestContext().setAttribute(CmsRequestContext.ATTRIBUTE_MODEL, modelResource.getRootPath());
413        }
414        CmsResource createdResource = rootCms.createResource(
415            creationPath,
416            getType(),
417            null,
418            new ArrayList<CmsProperty>(0));
419        if (modelResource != null) {
420            // set the model resource
421            CmsCategoryService.getInstance().copyCategories(rootCms, modelResource, creationPath);
422        }
423        try {
424            rootCms.unlockResource(creationPath);
425        } catch (CmsLockException e) {
426            // probably the parent folder is locked
427            LOG.info(e.getLocalizedMessage(), e);
428        }
429        return createdResource;
430    }
431
432    /**
433     * Creates a new element.<p>
434     *
435     * @param userCms the CMS context to use
436     * @param pageFolderRootPath root path of the folder containing the current container page
437     *
438     * @return the created resource
439     *
440     * @throws CmsException if something goes wrong
441     */
442    public CmsResource createNewElement(CmsObject userCms, String pageFolderRootPath) throws CmsException {
443
444        return createNewElement(userCms, null, pageFolderRootPath);
445    }
446
447    /**
448     * Gets the visibility status in the 'add' menu for this type and the given element view.<p>
449     *
450     * @param elementViewId the id of the view for which to compute the visibility status
451     * @param menuType the menu type for which we want to evaluate the visibility
452     *
453     * @return the visibility status
454     */
455    public AddMenuVisibility getAddMenuVisibility(CmsUUID elementViewId, AddMenuType menuType) {
456
457        if (isAddDisabled()) {
458            return AddMenuVisibility.disabled;
459        }
460
461        if (elementViewId.equals(getElementView())) {
462            if (isCreateDisabled() && (menuType == AddMenuType.ade)) {
463                return AddMenuVisibility.createDisabled;
464            }
465            return AddMenuVisibility.visible;
466        }
467
468        if (isShowInDefaultView() && elementViewId.equals(CmsElementView.DEFAULT_ELEMENT_VIEW.getId())) {
469            return AddMenuVisibility.fromOtherView;
470        }
471
472        return AddMenuVisibility.disabled;
473    }
474
475    /**
476     * Returns the bundle that is configured as workplace bundle for the resource type, or <code>null</code> if none is configured.
477     * @return the bundle that is configured as workplace bundle for the resource type, or <code>null</code> if none is configured.
478     */
479    public String getConfiguredWorkplaceBundle() {
480
481        return m_localization;
482    }
483
484    /**
485     * Gets the element delete mode.<p>
486     *
487     * @return the element delete mode
488     */
489    public ElementDeleteMode getElementDeleteMode() {
490
491        return m_elementDeleteMode;
492    }
493
494    /**
495     * Returns the element view id.<p>
496     *
497     * @return the element view id
498     */
499    public CmsUUID getElementView() {
500
501        return m_elementView == null ? CmsElementView.DEFAULT_ELEMENT_VIEW.getId() : m_elementView;
502    }
503
504    /**
505     * Computes the folder path for this resource type.<p>
506     *
507     * @param cms the cms context to use
508     * @param pageFolderRootPath root path of the folder containing the current container page
509     *
510     * @return the folder root path for this resource type
511     */
512    public String getFolderPath(CmsObject cms, String pageFolderRootPath) {
513
514        checkInitialized();
515        if (m_folderOrName != null) {
516            return m_folderOrName.getFolderPath(cms, pageFolderRootPath);
517        } else {
518            String siteRoot = null;
519            if (pageFolderRootPath != null) {
520                siteRoot = OpenCms.getSiteManager().getSiteRoot(pageFolderRootPath);
521            }
522            if (siteRoot == null) {
523                siteRoot = cms.getRequestContext().getSiteRoot();
524            }
525            return CmsStringUtil.joinPaths(siteRoot, CmsADEManager.CONTENT_FOLDER_NAME, m_typeName);
526        }
527    }
528
529    /**
530     * @see org.opencms.ade.configuration.I_CmsConfigurationObject#getKey()
531     */
532    public String getKey() {
533
534        return m_typeName;
535    }
536
537    /**
538     * Gets the name pattern.<p>
539     *
540     * @param useDefaultIfEmpty if true, uses a default value if the name pattern isn't set directly
541     *
542     * @return the name pattern
543     */
544    public String getNamePattern(boolean useDefaultIfEmpty) {
545
546        if (m_namePattern != null) {
547            return m_namePattern;
548        }
549        if (useDefaultIfEmpty) {
550            return m_typeName + "-%(number).xml";
551        }
552        return null;
553    }
554
555    /**
556     * Returns the number used for sorting module resource types.<p>
557     *
558     * @return the number used for sorting module resource types
559     */
560    public int getOrder() {
561
562        if (m_order == null) {
563            return I_CmsConfigurationObject.DEFAULT_ORDER;
564        }
565
566        return m_order.intValue();
567    }
568
569    /**
570     * Returns the order as an object (or null if it's not set).
571     *
572     * @return the order
573     */
574    public Integer getOrderObject() {
575
576        return m_order;
577    }
578
579    /**
580     * Gets the actual resource type for which this is a configuration.<p>
581     *
582     * @return the actual resource type
583     *
584     * @throws CmsException if something goes wrong
585     */
586    public I_CmsResourceType getType() throws CmsException {
587
588        return OpenCms.getResourceManager().getResourceType(m_typeName);
589    }
590
591    /**
592     * Returns the type name.<p>
593     *
594     * @return the type name
595     */
596    public String getTypeName() {
597
598        return m_typeName;
599    }
600
601    /**
602     * Checks if the type can be used for the given template context key.
603     *
604     * <p>If this type isn't specifically associated with one or more template keys, this returns true,
605     * otherwise it will check if the 'template' argument is among the template keys
606     *
607     * @param template the template key to check
608     * @return true if the type should be available for the template
609     */
610    public boolean isAvailableInTemplate(String template) {
611
612        return (template == null) || (m_templates.size() == 0) || m_templates.contains(template);
613    }
614
615    /**
616     * Initializes this instance.<p>
617     *
618     * @param cms the CMS context to use
619     */
620    public void initialize(CmsObject cms) {
621
622        m_cms = cms;
623
624    }
625
626    /**
627     * Returns true if adding elements of this type via ADE should be disabled.<p>
628     *
629     * @return true if elements of this type shouldn't be added to the page
630     */
631    public boolean isAddDisabled() {
632
633        return m_addDisabled;
634    }
635
636    /**
637     * Returns if elements of this type when used in models should be copied instead of reused.<p>
638     *
639     * @return if elements of this type when used in models should be copied instead of reused
640     */
641    public boolean isCopyInModels() {
642
643        return (m_copyInModels == null) || m_copyInModels.booleanValue();
644    }
645
646    /**
647     * Returns whether creating elements of this type via ADE should be disabled.<p>
648     *
649     * @return <code>true</code> if creating elements of this type via ADE should be disabled
650     */
651    public boolean isCreateDisabled() {
652
653        return m_createDisabled;
654    }
655
656    /**
657     * True if the detail page creation should be disabled for this resource type.<p>
658     *
659     * @return true if detail page creation should be disabled for this type
660     */
661    public boolean isDetailPagesDisabled() {
662
663        return m_detailPagesDisabled;
664    }
665
666    /**
667     * @see org.opencms.ade.configuration.I_CmsConfigurationObject#isDisabled()
668     */
669    public boolean isDisabled() {
670
671        return m_disabled;
672    }
673
674    /**
675     * Checks if editing should be disabled for container elements of this type.
676     *
677     * @return true if editing should be disabled for container elements of this type
678     */
679    public boolean isEditDisabled() {
680
681        return m_editDisabled;
682    }
683
684    /**
685     * Checks if creating and editing resources of this type should be possible via the edit buttons generated by lists.
686     *
687     * @return true if creating/editing resources of this type in lists should be possible
688     */
689    public boolean isEnabledInLists() {
690
691        return m_enableInLists;
692    }
693
694    /**
695     * Returns true if this resource type is configured as 'page relative', i.e. elements of this type are to be stored
696     * with the container page on which they were created.<p>
697     *
698     * @return true if this is a page relative type configuration
699     */
700    public boolean isPageRelative() {
701
702        return (m_folderOrName != null) && m_folderOrName.isPageRelative();
703    }
704
705    /**
706     * Returns true if the type should be shown in the default view if it is not assigned to it.<p>
707     *
708     * This defaults to 'false' if not set.
709     *
710     * @return true if the type should be shown in the default view event if  it doens't belong to that element view
711     */
712    public boolean isShowInDefaultView() {
713
714        return (m_showInDefaultView != null) && m_showInDefaultView.booleanValue();
715    }
716
717    /**
718     * If 'template' is not null, returns a copy of this type bean, but adds 'template' to the
719     * set of supported templates in the copy.
720     *
721     * @param template a template context key
722     * @return a new copy associated with the given template key
723     */
724    public CmsResourceTypeConfig markWithTemplate(String template) {
725
726        try {
727            if (template == null) {
728                return this;
729            }
730            CmsResourceTypeConfig result = (CmsResourceTypeConfig)super.clone();
731            HashSet<String> templates = new HashSet<>();
732            templates.add(template);
733            result.m_templates = templates;
734            return result;
735
736        } catch (CloneNotSupportedException e) {
737            return null;
738        }
739    }
740
741    /**
742     * @see org.opencms.ade.configuration.I_CmsConfigurationObject#merge(org.opencms.ade.configuration.I_CmsConfigurationObject)
743     */
744    public CmsResourceTypeConfig merge(CmsResourceTypeConfig childConfig) {
745
746        CmsContentFolderDescriptor folderOrName = childConfig.m_folderOrName != null
747        ? childConfig.m_folderOrName
748        : m_folderOrName;
749        String namePattern = childConfig.m_namePattern != null ? childConfig.m_namePattern : m_namePattern;
750        CmsUUID elementView = childConfig.m_elementView != null ? childConfig.m_elementView : m_elementView;
751        Boolean showInDefaultView = childConfig.m_showInDefaultView != null
752        ? childConfig.m_showInDefaultView
753        : m_showInDefaultView;
754        Boolean copyInModels = childConfig.m_copyInModels != null ? childConfig.m_copyInModels : m_copyInModels;
755        ElementDeleteMode deleteMode = childConfig.m_elementDeleteMode != null
756        ? childConfig.m_elementDeleteMode
757        : m_elementDeleteMode;
758        Integer order = childConfig.m_order != null ? childConfig.m_order : m_order;
759
760        boolean mergedDisabled = childConfig.m_availabilityNotSet ? isDisabled() : childConfig.isDisabled();
761        boolean mergedAddDisabled = childConfig.m_availabilityNotSet ? isAddDisabled() : childConfig.isAddDisabled();
762        boolean mergedCreateDisabled = childConfig.m_availabilityNotSet
763        ? isCreateDisabled()
764        : (isCreateDisabled() || childConfig.isCreateDisabled());
765
766        boolean mergedEnableInLists = childConfig.m_availabilityNotSet ? m_enableInLists : childConfig.m_enableInLists;
767        boolean mergedDisableEdit = childConfig.m_availabilityNotSet ? m_editDisabled : childConfig.m_editDisabled;
768
769        CmsResourceTypeConfig result = new CmsResourceTypeConfig(
770            m_typeName,
771            mergedDisabled,
772            folderOrName,
773            namePattern,
774            isDetailPagesDisabled() || childConfig.isDetailPagesDisabled(),
775            mergedAddDisabled,
776            // a type marked as not creatable, should not be creatable in any sub site
777            mergedCreateDisabled,
778            mergedDisableEdit,
779            mergedEnableInLists,
780            false /* availabilityNotSet - doesn't matter what we use here, because we do not use the return value of this method as a child for configuration merging (which is the only way this attribute is used) */,
781            elementView,
782            m_localization,
783            showInDefaultView,
784            copyInModels,
785            order,
786            deleteMode);
787        result.m_templates = new HashSet<>(this.m_templates);
788        result.m_templates.addAll(childConfig.m_templates);
789        return result;
790    }
791
792    /**
793     * @see java.lang.Object#toString()
794     */
795    @Override
796    public String toString() {
797
798        return getClass().getSimpleName() + "[" + m_typeName + "]";
799    }
800
801    /**
802     * Creates a shallow copy of this resource type configuration object.<p>
803     *
804     * @return a copy of the resource type configuration object
805     */
806    protected CmsResourceTypeConfig copy() {
807
808        return copy(false);
809    }
810
811    /**
812     * Creates a shallow copy of this resource type configuration object.<p>
813     *
814     * @param disabled true if the copy should be disabled regardless of whether the original is disabled
815     *
816     * @return a copy of the resource type configuration object
817     */
818    protected CmsResourceTypeConfig copy(boolean disabled) {
819
820        CmsResourceTypeConfig result = new CmsResourceTypeConfig(
821            m_typeName,
822            m_disabled || disabled,
823            getFolderOrName(),
824            m_namePattern,
825            m_detailPagesDisabled,
826            isAddDisabled(),
827            isCreateDisabled(),
828            m_editDisabled,
829            m_enableInLists,
830            m_availabilityNotSet,
831            m_elementView,
832            m_localization,
833            m_showInDefaultView,
834            m_copyInModels,
835            m_order,
836            m_elementDeleteMode);
837        result.m_templates = m_templates;
838        return result;
839    }
840
841    /**
842     * Returns the folder bean from the configuration.<p>
843     *
844     * Normally, you should use getFolderPath() instead.<p>
845     *
846     * @return the folder bean from the configuration
847     */
848    protected CmsContentFolderDescriptor getFolderOrName() {
849
850        return m_folderOrName;
851    }
852
853    /**
854     * Gets the configured name pattern.<p>
855     *
856     * @return the configured name pattern
857     */
858    protected String getNamePattern() {
859
860        return m_namePattern;
861    }
862
863    /**
864     * Creates a new CMS object based on existing one and changes its site root to the site root.<p>
865     *
866     * @param cms the CMS context
867     * @return the root site CMS context
868     * @throws CmsException if something goes wrong
869     */
870    protected CmsObject rootCms(CmsObject cms) throws CmsException {
871
872        CmsObject result = OpenCms.initCmsObject(cms);
873        result.getRequestContext().setSiteRoot("");
874        return result;
875    }
876
877    /**
878     * Tries to remove a lock on an ancestor of a given path owned by the current user.<p>
879     *
880     * @param cms the CMS context
881     * @param folderPath the path for which the lock should be removed
882     *
883     * @throws CmsException if something goes wrong
884     */
885    protected void tryToUnlock(CmsObject cms, String folderPath) throws CmsException {
886
887        // Get path of first ancestor that actually exists
888        while (!cms.existsResource(folderPath)) {
889            folderPath = CmsResource.getParentFolder(folderPath);
890        }
891        CmsResource resource = cms.readResource(folderPath);
892        CmsLock lock = cms.getLock(resource);
893        // we are only interested in locks we can safely unlock, i.e. locks by the current user
894        if (lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) {
895            // walk up the tree until we get to the location from which the lock is inherited
896            while (lock.isInherited()) {
897                folderPath = CmsResource.getParentFolder(folderPath);
898                resource = cms.readResource(folderPath);
899                lock = cms.getLock(resource);
900            }
901            cms.unlockResource(folderPath);
902        }
903    }
904
905    /**
906     * Updates the base path for the folder information.<p>
907     *
908     * @param basePath the new base path
909     */
910    protected void updateBasePath(String basePath) {
911
912        if (m_folderOrName != null) {
913            if (m_folderOrName.isName()) {
914                m_folderOrName = new CmsContentFolderDescriptor(basePath, m_folderOrName.getFolderName());
915            }
916        } else {
917            m_folderOrName = new CmsContentFolderDescriptor(basePath, m_typeName);
918        }
919    }
920}