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