001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.workplace.tools.modules;
029
030import org.opencms.ade.configuration.CmsADEManager;
031import org.opencms.ade.configuration.CmsConfigurationReader;
032import org.opencms.ade.configuration.formatters.CmsFormatterBeanParser;
033import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCache;
034import org.opencms.file.CmsFile;
035import org.opencms.file.CmsObject;
036import org.opencms.file.CmsProject;
037import org.opencms.file.CmsResource;
038import org.opencms.file.CmsUser;
039import org.opencms.file.types.CmsResourceTypeFolder;
040import org.opencms.file.types.CmsResourceTypeXmlContent;
041import org.opencms.file.types.I_CmsResourceType;
042import org.opencms.i18n.CmsLocaleManager;
043import org.opencms.i18n.CmsVfsBundleManager;
044import org.opencms.lock.CmsLock;
045import org.opencms.main.CmsEvent;
046import org.opencms.main.CmsException;
047import org.opencms.main.CmsIllegalArgumentException;
048import org.opencms.main.CmsLog;
049import org.opencms.main.I_CmsEventListener;
050import org.opencms.main.OpenCms;
051import org.opencms.module.CmsModule;
052import org.opencms.module.Messages;
053import org.opencms.report.A_CmsReportThread;
054import org.opencms.report.I_CmsReport;
055import org.opencms.util.CmsFileUtil;
056import org.opencms.util.CmsStringUtil;
057import org.opencms.workplace.explorer.CmsExplorerTypeSettings;
058import org.opencms.xml.content.CmsVfsBundleLoaderXml;
059import org.opencms.xml.content.CmsXmlContent;
060import org.opencms.xml.content.CmsXmlContentFactory;
061import org.opencms.xml.types.I_CmsXmlContentValue;
062
063import java.io.UnsupportedEncodingException;
064import java.util.ArrayList;
065import java.util.Collections;
066import java.util.HashMap;
067import java.util.List;
068import java.util.Locale;
069import java.util.Map;
070import java.util.Map.Entry;
071
072import org.apache.commons.logging.Log;
073
074import org.dom4j.Element;
075
076/**
077 * The report thread to add a new resource type to a module and publish all related resources.<p>
078 */
079public class CmsModuleAddResourceTypeThread extends A_CmsReportThread {
080
081    /** Message key prefix. */
082    private static final String KEY_PREFIX_DESCRIPTION = "desc.";
083
084    /** Message key prefix. */
085    private static final String KEY_PREFIX_NAME = "fileicon.";
086
087    /** Message key prefix. */
088    private static final String KEY_PREFIX_TITLE = "title.";
089
090    /** The log object for this class. */
091    private static final Log LOG = CmsLog.getLog(CmsModuleAddResourceTypeThread.class);
092
093    /** Message bundle folder name. */
094    private static final String PATH_I18N = "i18n";
095
096    /** Message properties file encoding. */
097    private static final String PROPERTIES_ENCODING = "ISO-8859-1";
098
099    /** Message properties file name. */
100    private static final String PROPERTIES_FILE_NAME = "workplace.properties";
101
102    /** Sample file. */
103    private static final String SAMPLE_FORMATTER = "/system/modules/org.opencms.workplace.tools.modules/samples/sample-formatter.jsp";
104
105    /** Sample file. */
106    private static final String SAMPLE_ICON_BIG = "/system/modules/org.opencms.workplace.tools.modules/samples/sample-icon_big.png";
107
108    /** Sample file. */
109    private static final String SAMPLE_ICON_SMALL = "/system/modules/org.opencms.workplace.tools.modules/samples/sample-icon.png";
110
111    /** Sample file. */
112    private static final String SAMPLE_SCHEMA = "/system/modules/org.opencms.workplace.tools.modules/samples/sample-schema.xsd";
113
114    /** The sample schema type name. */
115    private static final String SAMPLE_SCHEMA_TYPE_NAME = "SampleType";
116
117    /** Message bundle file name suffix. */
118    private static final String SUFFIX_BUNDLE_FILE = ".workplace";
119
120    /** The resource type information. */
121    private CmsResourceTypeInfoBean m_resInfo;
122
123    /**
124     * Constructor.<p>
125     *
126     * @param cms the cms context
127     * @param resInfo the resource type information
128     */
129    protected CmsModuleAddResourceTypeThread(CmsObject cms, CmsResourceTypeInfoBean resInfo) {
130
131        super(cms, resInfo.getName());
132        m_resInfo = resInfo;
133        initHtmlReport(cms.getRequestContext().getLocale());
134    }
135
136    /**
137     * @see org.opencms.report.A_CmsReportThread#getReportUpdate()
138     */
139    @Override
140    public String getReportUpdate() {
141
142        return getReport().getReportUpdate();
143    }
144
145    /**
146     * @see java.lang.Thread#run()
147     */
148    @Override
149    public void run() {
150
151        CmsObject cms = getCms();
152        CmsProject currentProject = cms.getRequestContext().getCurrentProject();
153        try {
154            CmsProject workProject = cms.createProject(
155                "Add_resource_type_project",
156                "Add resource type project",
157                OpenCms.getDefaultUsers().getGroupAdministrators(),
158                OpenCms.getDefaultUsers().getGroupAdministrators(),
159                CmsProject.PROJECT_TYPE_TEMPORARY);
160            cms.getRequestContext().setCurrentProject(workProject);
161            CmsModule module = OpenCms.getModuleManager().getModule(m_resInfo.getModuleName()).clone();
162            String moduleFolder = CmsStringUtil.joinPaths("/system/modules/", m_resInfo.getModuleName());
163            copySampleFiles(module, moduleFolder);
164            List<I_CmsResourceType> types = new ArrayList<I_CmsResourceType>(module.getResourceTypes());
165            // create the new resource type
166            CmsResourceTypeXmlContent type = new CmsResourceTypeXmlContent();
167            type.addConfigurationParameter(CmsResourceTypeXmlContent.CONFIGURATION_SCHEMA, m_resInfo.getSchema());
168            type.setAdditionalModuleResourceType(true);
169            type.setModuleName(m_resInfo.getModuleName());
170            type.initConfiguration(
171                m_resInfo.getName(),
172                String.valueOf(m_resInfo.getId()),
173                CmsResourceTypeXmlContent.class.getName());
174            types.add(type);
175            module.setResourceTypes(types);
176
177            List<CmsExplorerTypeSettings> settings = new ArrayList<CmsExplorerTypeSettings>(module.getExplorerTypes());
178            // create the matching explorer type
179            CmsExplorerTypeSettings setting = new CmsExplorerTypeSettings();
180            setting.setTypeAttributes(
181                m_resInfo.getName(),
182                m_resInfo.getNiceName(),
183                m_resInfo.getSmallIcon(),
184                m_resInfo.getBigIcon(),
185                null,
186                null,
187                "xmlcontent",
188                null,
189                "false",
190                null,
191                null);
192            setting.setAutoSetNavigation("false");
193            setting.setAutoSetTitle("false");
194            setting.setNewResourceOrder("10");
195            setting.setAddititionalModuleExplorerType(true);
196            addTypeMessages(setting, moduleFolder);
197            settings.add(setting);
198            module.setExplorerTypes(settings);
199            createSampleFormatter(moduleFolder);
200            // now unlock and publish the project
201            getReport().println(
202                Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0),
203                I_CmsReport.FORMAT_HEADLINE);
204            cms.unlockProject(workProject.getUuid());
205            OpenCms.getPublishManager().publishProject(cms, getReport());
206            OpenCms.getPublishManager().waitWhileRunning();
207
208            getReport().println(
209                Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0),
210                I_CmsReport.FORMAT_HEADLINE);
211
212            // write the module configuration, init resource types, explorer types and clear OpenCms caches
213            OpenCms.getModuleManager().updateModule(cms, module);
214            OpenCms.getResourceManager().initialize(cms);
215            OpenCms.getWorkplaceManager().addExplorerTypeSettings(module);
216            OpenCms.fireCmsEvent(
217                new CmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, Collections.<String, Object> emptyMap()));
218            // re-initialize the workplace
219            OpenCms.getWorkplaceManager().initialize(getCms());
220        } catch (Exception e) {
221            LOG.error(e.getLocalizedMessage(), e);
222            getReport().addError(e);
223        } finally {
224            cms.getRequestContext().setCurrentProject(currentProject);
225        }
226    }
227
228    /**
229     * Adds the given messages to the workplace properties file.<p>
230     *
231     * @param messages the messages
232     * @param propertiesFile the properties file
233     * @param forcePropertyFileEncoding flag, indicating if encoding {@link #PROPERTIES_ENCODING} should be forced
234     *
235     * @throws CmsException if writing the properties fails
236     * @throws UnsupportedEncodingException in case of encoding issues
237     */
238    private void addMessagesToPropertiesFile(
239        Map<String, String> messages,
240        CmsFile propertiesFile,
241        boolean forcePropertyFileEncoding)
242    throws CmsException, UnsupportedEncodingException {
243
244        lockTemporary(propertiesFile);
245        String encoding = forcePropertyFileEncoding
246        ? PROPERTIES_ENCODING
247        : CmsFileUtil.getEncoding(getCms(), propertiesFile);
248        StringBuffer contentBuffer = new StringBuffer();
249        contentBuffer.append(new String(propertiesFile.getContents(), encoding));
250        for (Entry<String, String> entry : messages.entrySet()) {
251            contentBuffer.append("\n");
252            contentBuffer.append(entry.getKey());
253            contentBuffer.append("=");
254            contentBuffer.append(entry.getValue());
255        }
256        contentBuffer.append("\n");
257        propertiesFile.setContents(contentBuffer.toString().getBytes(encoding));
258        getCms().writeFile(propertiesFile);
259    }
260
261    /**
262     * Adds the given messages to the vfs message bundle.<p>
263     *
264     * @param messages the messages
265     * @param vfsBundleFile the bundle file
266     *
267     * @throws CmsException if something goes wrong writing the file
268     */
269    private void addMessagesToVfsBundle(Map<String, String> messages, CmsFile vfsBundleFile) throws CmsException {
270
271        lockTemporary(vfsBundleFile);
272        CmsObject cms = getCms();
273        CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, vfsBundleFile);
274        Locale locale = CmsLocaleManager.getDefaultLocale();
275        if (!content.hasLocale(locale)) {
276            content.addLocale(cms, locale);
277        }
278        Element root = content.getLocaleNode(locale);
279        for (Entry<String, String> entry : messages.entrySet()) {
280            Element message = root.addElement(CmsVfsBundleLoaderXml.N_MESSAGE);
281            Element key = message.addElement(CmsVfsBundleLoaderXml.N_KEY);
282            key.setText(entry.getKey());
283            Element value = message.addElement(CmsVfsBundleLoaderXml.N_VALUE);
284            value.setText(entry.getValue());
285        }
286        content.initDocument();
287        vfsBundleFile.setContents(content.marshal());
288        cms.writeFile(vfsBundleFile);
289    }
290
291    /**
292     * Adds the explorer type messages to the modules workplace bundle.<p>
293     *
294     * @param setting the explorer type settings
295     * @param moduleFolder the module folder name
296     *
297     * @throws CmsException if writing the bundle fails
298     * @throws UnsupportedEncodingException in case of encoding issues
299     */
300    private void addTypeMessages(CmsExplorerTypeSettings setting, String moduleFolder)
301    throws CmsException, UnsupportedEncodingException {
302
303        Map<String, String> messages = new HashMap<String, String>();
304        CmsObject cms = getCms();
305        // check if any messages to set
306        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_resInfo.getNiceName())) {
307            String key = KEY_PREFIX_NAME + m_resInfo.getName();
308            messages.put(key, m_resInfo.getNiceName());
309            setting.setKey(key);
310        }
311        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_resInfo.getDescription())) {
312            String key = KEY_PREFIX_DESCRIPTION + m_resInfo.getName();
313            messages.put(key, m_resInfo.getDescription());
314            setting.setInfo(key);
315        }
316        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_resInfo.getTitle())) {
317            String key = KEY_PREFIX_TITLE + m_resInfo.getName();
318            messages.put(key, m_resInfo.getTitle());
319            setting.setTitleKey(key);
320        }
321        if (!messages.isEmpty()) {
322            // add the messages to the module's workplace bundle
323
324            //1. check if the bundle exists as raw Java bundle
325            String workplacePropertiesFile = CmsStringUtil.joinPaths(
326                moduleFolder,
327                CmsModulesEditBase.PATH_CLASSES,
328                m_resInfo.getModuleName().replace(".", "/"),
329                PROPERTIES_FILE_NAME);
330            if (cms.existsResource(workplacePropertiesFile)) {
331                addMessagesToPropertiesFile(messages, cms.readFile(workplacePropertiesFile), true);
332            } else {
333                //2. check if the bundle exists as XML bundle
334                String vfsBundleFileName = CmsStringUtil.joinPaths(
335                    moduleFolder,
336                    PATH_I18N,
337                    m_resInfo.getModuleName() + SUFFIX_BUNDLE_FILE);
338                OpenCms.getLocaleManager();
339                if (cms.existsResource(vfsBundleFileName)) {
340                    addMessagesToVfsBundle(messages, cms.readFile(vfsBundleFileName));
341                } else {
342                    //3. check if the bundle exists as property bundle
343                    // we always write to the default locale
344                    String propertyBundleFileName = vfsBundleFileName + "_" + CmsLocaleManager.getDefaultLocale();
345                    if (!cms.existsResource(propertyBundleFileName)) {
346                        //if non of the checked bundles exist, create one.
347                        String bundleFolder = CmsStringUtil.joinPaths(moduleFolder, PATH_I18N);
348                        if (!cms.existsResource(bundleFolder)) {
349                            cms.createResource(
350                                bundleFolder,
351                                OpenCms.getResourceManager().getResourceType(
352                                    CmsResourceTypeFolder.getStaticTypeName()));
353                        }
354                        CmsResource res = cms.createResource(
355                            propertyBundleFileName,
356                            OpenCms.getResourceManager().getResourceType(CmsVfsBundleManager.TYPE_PROPERTIES_BUNDLE),
357                            null,
358                            null);
359                        cms.writeResource(res);
360                    }
361                    addMessagesToPropertiesFile(messages, cms.readFile(propertyBundleFileName), false);
362                }
363
364            }
365        }
366    }
367
368    /**
369     * Copies sample schema and resource type icons and adds the resources to the module.<p>
370     *
371     * @param module the module
372     * @param moduleFolder the module folder name
373     *
374     * @throws CmsIllegalArgumentException in case something goes wrong copying the resources
375     * @throws CmsException in case something goes wrong copying the resources
376     */
377    private void copySampleFiles(CmsModule module, String moduleFolder)
378    throws CmsIllegalArgumentException, CmsException {
379
380        CmsObject cms = getCms();
381        List<String> moduleResource = new ArrayList<String>(module.getResources());
382        if (!cms.existsResource(moduleFolder)) {
383            cms.createResource(moduleFolder, CmsResourceTypeFolder.getStaticTypeId());
384            moduleResource.add(moduleFolder);
385        }
386        String schemaFolder = CmsStringUtil.joinPaths(moduleFolder, "schemas");
387        if (!cms.existsResource(schemaFolder)) {
388            cms.createResource(schemaFolder, CmsResourceTypeFolder.getStaticTypeId());
389        }
390        String schemaFile = CmsStringUtil.joinPaths(schemaFolder, m_resInfo.getName() + ".xsd");
391        if (!cms.existsResource(schemaFile)) {
392            cms.copyResource(SAMPLE_SCHEMA, schemaFile, CmsResource.COPY_AS_NEW);
393            if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_resInfo.getSchemaTypeName())) {
394                // replace the sample schema type name with the provided name
395                try {
396                    CmsFile schema = cms.readFile(schemaFile);
397                    OpenCms.getLocaleManager();
398                    String schemaContent = new String(
399                        schema.getContents(),
400                        CmsLocaleManager.getResourceEncoding(cms, schema));
401                    schemaContent = schemaContent.replaceAll(SAMPLE_SCHEMA_TYPE_NAME, m_resInfo.getSchemaTypeName());
402                    schema.setContents(schemaContent.getBytes());
403                    cms.writeFile(schema);
404                } catch (Exception e) {
405                    LOG.error(e.getLocalizedMessage(), e);
406                    getReport().addError(e);
407                }
408            }
409
410        }
411        m_resInfo.setSchema(schemaFile);
412        String filetypesFolder = "/system/workplace/resources/filetypes/";
413        String smallIcon = CmsStringUtil.joinPaths(filetypesFolder, m_resInfo.getName() + ".png");
414        if (!cms.existsResource(smallIcon)) {
415            cms.copyResource(SAMPLE_ICON_SMALL, smallIcon, CmsResource.COPY_AS_NEW);
416            moduleResource.add(smallIcon);
417        }
418        m_resInfo.setSmallIcon(m_resInfo.getName() + ".png");
419        String bigIcon = CmsStringUtil.joinPaths(filetypesFolder, m_resInfo.getName() + "_big.png");
420        if (!cms.existsResource(bigIcon)) {
421            cms.copyResource(SAMPLE_ICON_BIG, bigIcon, CmsResource.COPY_AS_NEW);
422            moduleResource.add(bigIcon);
423        }
424        m_resInfo.setBigIcon(m_resInfo.getName() + "_big.png");
425        module.setResources(moduleResource);
426    }
427
428    /**
429     * Copies the sample formatter JSP, creates the associated formatter and module configuration.<p>
430     *
431     * @param moduleFolder the module folder name
432     *
433     * @throws CmsIllegalArgumentException in case something goes wrong copying the resources
434     * @throws CmsException in case something goes wrong copying the resources
435     */
436    private void createSampleFormatter(String moduleFolder) throws CmsIllegalArgumentException, CmsException {
437
438        CmsObject cms = getCms();
439        String formatterFolder = CmsStringUtil.joinPaths(moduleFolder, CmsModulesEditBase.PATH_FORMATTERS);
440        if (!cms.existsResource(formatterFolder)) {
441            cms.createResource(formatterFolder, CmsResourceTypeFolder.getStaticTypeId());
442        }
443        String formatterJSP = CmsStringUtil.joinPaths(formatterFolder, m_resInfo.getName() + "-formatter.jsp");
444        if (!cms.existsResource(formatterJSP)) {
445            cms.copyResource(SAMPLE_FORMATTER, formatterJSP, CmsResource.COPY_AS_NEW);
446        }
447        String formatterConfig = CmsStringUtil.joinPaths(
448            formatterFolder,
449            m_resInfo.getName() + "-formatter-config.xml");
450        if (!cms.existsResource(formatterConfig)) {
451
452            cms.createResource(
453                formatterConfig,
454                OpenCms.getResourceManager().getResourceType(
455                    CmsFormatterConfigurationCache.TYPE_FORMATTER_CONFIG).getTypeId());
456            CmsFile configFile = cms.readFile(formatterConfig);
457            CmsXmlContent configContent = CmsXmlContentFactory.unmarshal(cms, configFile);
458            if (!configContent.hasLocale(CmsConfigurationReader.DEFAULT_LOCALE)) {
459                configContent.addLocale(cms, CmsConfigurationReader.DEFAULT_LOCALE);
460            }
461            I_CmsXmlContentValue typeValue = configContent.getValue(
462                CmsFormatterBeanParser.N_TYPE,
463                CmsConfigurationReader.DEFAULT_LOCALE);
464            typeValue.setStringValue(cms, m_resInfo.getName());
465            I_CmsXmlContentValue formatterValue = configContent.getValue(
466                CmsFormatterBeanParser.N_JSP,
467                CmsConfigurationReader.DEFAULT_LOCALE);
468            formatterValue.setStringValue(cms, formatterJSP);
469            I_CmsXmlContentValue formatterNameValue = configContent.getValue(
470                CmsFormatterBeanParser.N_NICE_NAME,
471                CmsConfigurationReader.DEFAULT_LOCALE);
472            formatterNameValue.setStringValue(
473                cms,
474                "Sample formatter for "
475                    + (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_resInfo.getNiceName())
476                    ? m_resInfo.getNiceName()
477                    : m_resInfo.getName()));
478            // set matching container width to '-1' to fit everywhere
479            configContent.addValue(cms, CmsFormatterBeanParser.N_MATCH, CmsConfigurationReader.DEFAULT_LOCALE, 0);
480            configContent.addValue(
481                cms,
482                CmsFormatterBeanParser.N_MATCH + "/" + CmsFormatterBeanParser.N_WIDTH,
483                CmsConfigurationReader.DEFAULT_LOCALE,
484                0);
485            I_CmsXmlContentValue widthValue = configContent.getValue(
486                CmsFormatterBeanParser.N_MATCH
487                    + "/"
488                    + CmsFormatterBeanParser.N_WIDTH
489                    + "/"
490                    + CmsFormatterBeanParser.N_WIDTH,
491                CmsConfigurationReader.DEFAULT_LOCALE);
492            widthValue.setStringValue(cms, "-1");
493
494            // enable the formatter
495            I_CmsXmlContentValue enabledValue = configContent.getValue(
496                CmsFormatterBeanParser.N_AUTO_ENABLED,
497                CmsConfigurationReader.DEFAULT_LOCALE);
498            enabledValue.setStringValue(cms, Boolean.TRUE.toString());
499            configFile.setContents(configContent.marshal());
500            cms.writeFile(configFile);
501        }
502        String moduleConfig = CmsStringUtil.joinPaths(moduleFolder, ".config");
503        if (!cms.existsResource(moduleConfig)) {
504            cms.createResource(
505                moduleConfig,
506                OpenCms.getResourceManager().getResourceType(CmsADEManager.MODULE_CONFIG_TYPE).getTypeId());
507        }
508        CmsFile moduleConfigFile = cms.readFile(moduleConfig);
509        lockTemporary(moduleConfigFile);
510        CmsXmlContent moduleConfigContent = CmsXmlContentFactory.unmarshal(cms, moduleConfigFile);
511        I_CmsXmlContentValue resourceTypeValue = moduleConfigContent.addValue(
512            cms,
513            CmsConfigurationReader.N_RESOURCE_TYPE,
514            CmsConfigurationReader.DEFAULT_LOCALE,
515            0);
516        I_CmsXmlContentValue typeValue = moduleConfigContent.getValue(
517            resourceTypeValue.getPath() + "/" + CmsConfigurationReader.N_TYPE_NAME,
518            CmsConfigurationReader.DEFAULT_LOCALE);
519        typeValue.setStringValue(cms, m_resInfo.getName());
520        moduleConfigFile.setContents(moduleConfigContent.marshal());
521        cms.writeFile(moduleConfigFile);
522    }
523
524    /**
525     * Locks the given resource temporarily.<p>
526     *
527     * @param resource the resource to lock
528     *
529     * @throws CmsException if locking fails
530     */
531    private void lockTemporary(CmsResource resource) throws CmsException {
532
533        CmsObject cms = getCms();
534        CmsUser user = cms.getRequestContext().getCurrentUser();
535        CmsLock lock = cms.getLock(resource);
536        if (!lock.isOwnedBy(user)) {
537            cms.lockResourceTemporary(resource);
538        } else if (!lock.isOwnedInProjectBy(user, cms.getRequestContext().getCurrentProject())) {
539            cms.changeLock(resource);
540        }
541    }
542}