Java tutorial
/* * This library is part of OpenCms - * the Open Source Content Management System * * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * For further information about Alkacon Software, please see the * company website: http://www.alkacon.com * * For further information about OpenCms, please see the * project website: http://www.opencms.org * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.opencms.workplace.tools.modules; import org.opencms.ade.configuration.CmsADEManager; import org.opencms.ade.configuration.formatters.CmsFormatterConfigurationCache; import org.opencms.configuration.CmsConfigurationCopyResource; import org.opencms.db.CmsExportPoint; import org.opencms.file.CmsFile; import org.opencms.file.CmsObject; import org.opencms.file.CmsProject; import org.opencms.file.CmsProperty; import org.opencms.file.CmsResource; import org.opencms.file.CmsResourceFilter; import org.opencms.file.CmsVfsResourceNotFoundException; import org.opencms.file.types.A_CmsResourceType; import org.opencms.file.types.CmsResourceTypeFolder; import org.opencms.file.types.CmsResourceTypeUnknown; import org.opencms.file.types.CmsResourceTypeXmlContainerPage; import org.opencms.file.types.CmsResourceTypeXmlContent; import org.opencms.file.types.I_CmsResourceType; import org.opencms.i18n.CmsLocaleManager; import org.opencms.i18n.CmsVfsBundleManager; import org.opencms.loader.CmsLoaderException; import org.opencms.lock.CmsLock; import org.opencms.main.CmsException; import org.opencms.main.CmsLog; import org.opencms.main.I_CmsEventListener; import org.opencms.main.OpenCms; import org.opencms.module.CmsModule; import org.opencms.module.Messages; import org.opencms.report.A_CmsReportThread; import org.opencms.report.I_CmsReport; import org.opencms.util.CmsStringUtil; import org.opencms.util.CmsUUID; import org.opencms.workplace.CmsWorkplace; import org.opencms.workplace.explorer.CmsExplorerTypeSettings; import org.opencms.xml.CmsXmlException; import org.opencms.xml.content.CmsXmlContent; import org.opencms.xml.content.CmsXmlContentFactory; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.StringTokenizer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import com.google.common.base.Function; import com.google.common.base.Functions; /** * The report thread to clone a module.<p> */ public class CmsCloneModuleThread extends A_CmsReportThread { /** * String replacement function.<p> */ class ReplaceAll implements Function<String, String> { /** Regex to match. */ private String m_from; /** Replacement for the regex. */ private String m_to; /** * Creates a new instance.<p> * * @param from the regex to match * @param to the replacement */ public ReplaceAll(String from, String to) { m_from = from; m_to = to; } /** * @see com.google.common.base.Function#apply(java.lang.Object) */ public String apply(String input) { return input.replaceAll(m_from, m_to); } } /** The icon path. */ public static final String ICON_PATH = CmsWorkplace.VFS_PATH_RESOURCES + CmsWorkplace.RES_PATH_FILETYPES; /** Classes folder within the module. */ public static final String PATH_CLASSES = "classes/"; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsCloneModuleThread.class); /** The clone module information. */ private CmsCloneModuleInfo m_cloneInfo; /** * Constructor.<p> * * @param cms the cms context * @param cloneInfo the clone module information */ protected CmsCloneModuleThread(CmsObject cms, CmsCloneModuleInfo cloneInfo) { super(cms, cloneInfo.getName()); m_cloneInfo = cloneInfo; initHtmlReport(cms.getRequestContext().getLocale()); } /** * Returns a list of all module names.<p> * * @return a list of all module names */ public List<String> getAllModuleNames() { List<String> sortedModuleNames = new ArrayList<String>(OpenCms.getModuleManager().getModuleNames()); java.util.Collections.sort(sortedModuleNames); return sortedModuleNames; } /** * @see org.opencms.report.A_CmsReportThread#getReportUpdate() */ @Override public String getReportUpdate() { return getReport().getReportUpdate(); } /** * @see java.lang.Thread#run() */ @Override public void run() { CmsModule sourceModule = OpenCms.getModuleManager().getModule(m_cloneInfo.getSourceModuleName()); // clone the module object CmsModule targetModule = (CmsModule) sourceModule.clone(); targetModule.setName(m_cloneInfo.getName()); targetModule.setNiceName(m_cloneInfo.getNiceName()); targetModule.setDescription(m_cloneInfo.getDescription()); targetModule.setAuthorEmail(m_cloneInfo.getAuthorEmail()); targetModule.setAuthorName(m_cloneInfo.getAuthorName()); targetModule.setGroup(m_cloneInfo.getGroup()); targetModule.setActionClass(m_cloneInfo.getActionClass()); CmsObject cms = getCms(); CmsProject currentProject = cms.getRequestContext().getCurrentProject(); try { CmsProject workProject = cms.createProject("Clone_module_work_project", "Clone modulee work project", OpenCms.getDefaultUsers().getGroupAdministrators(), OpenCms.getDefaultUsers().getGroupAdministrators(), CmsProject.PROJECT_TYPE_TEMPORARY); cms.getRequestContext().setCurrentProject(workProject); // store the module paths String sourceModulePath = CmsWorkplace.VFS_PATH_MODULES + sourceModule.getName() + "/"; String targetModulePath = CmsWorkplace.VFS_PATH_MODULES + targetModule.getName() + "/"; // store the package name as path part String sourcePathPart = sourceModule.getName().replaceAll("\\.", "/"); String targetPathPart = targetModule.getName().replaceAll("\\.", "/"); // store the classes folder paths String sourceClassesPath = targetModulePath + PATH_CLASSES + sourcePathPart + "/"; String targetClassesPath = targetModulePath + PATH_CLASSES + targetPathPart + "/"; // copy the resources cms.copyResource(sourceModulePath, targetModulePath); // check if we have to create the classes folder if (cms.existsResource(sourceClassesPath)) { // in the source module a classes folder was defined, // now create all sub-folders for the package structure in the new module folder createTargetClassesFolder(targetModule, sourceClassesPath, targetModulePath + PATH_CLASSES); // delete the origin classes folder deleteSourceClassesFolder(targetModulePath, sourcePathPart, targetPathPart); } // TODO: clone module dependencies // adjust the export points cloneExportPoints(sourceModule, targetModule, sourcePathPart, targetPathPart); // adjust the resource type names and IDs Map<String, String> descKeys = new HashMap<String, String>(); Map<I_CmsResourceType, I_CmsResourceType> resTypeMap = cloneResourceTypes(sourceModule, targetModule, sourcePathPart, targetPathPart, descKeys); // adjust the explorer type names and store referred icons and message keys Map<String, String> iconPaths = new HashMap<String, String>(); cloneExplorerTypes(targetModule, iconPaths, descKeys); // rename the icon file names cloneExplorerTypeIcons(iconPaths); // adjust the module resources adjustModuleResources(sourceModule, targetModule, sourcePathPart, targetPathPart, iconPaths); // search and replace the localization keys if (getCms().existsResource(targetClassesPath)) { List<CmsResource> props = cms.readResources(targetClassesPath, CmsResourceFilter.DEFAULT_FILES); replacesMessages(descKeys, props); } int type = OpenCms.getResourceManager().getResourceType(CmsVfsBundleManager.TYPE_XML_BUNDLE) .getTypeId(); CmsResourceFilter filter = CmsResourceFilter.requireType(type); List<CmsResource> resources = cms.readResources(targetModulePath, filter); replacesMessages(descKeys, resources); renameXmlVfsBundles(resources, targetModule, sourceModule.getName()); List<CmsResource> allModuleResources = cms.readResources(targetModulePath, CmsResourceFilter.ALL); replacePath(sourceModulePath, targetModulePath, allModuleResources); // search and replace paths replaceModuleName(); // replace formatter paths if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_cloneInfo.getFormatterTargetModule()) && !targetModule.getResourceTypes().isEmpty()) { replaceFormatterPaths(targetModule); } adjustConfigs(targetModule, resTypeMap); // now unlock and publish the project getReport().println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE); cms.unlockProject(workProject.getUuid()); OpenCms.getPublishManager().publishProject(cms, getReport()); OpenCms.getPublishManager().waitWhileRunning(); getReport().println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0), I_CmsReport.FORMAT_HEADLINE); // add the imported module to the module manager OpenCms.getModuleManager().addModule(cms, targetModule); // reinitialize the resource manager with additional module resource types if necessary if (targetModule.getResourceTypes() != Collections.EMPTY_LIST) { OpenCms.getResourceManager().initialize(cms); } // reinitialize the workplace manager with additional module explorer types if necessary if (targetModule.getExplorerTypes() != Collections.EMPTY_LIST) { OpenCms.getWorkplaceManager().addExplorerTypeSettings(targetModule); } // re-initialize the workplace OpenCms.getWorkplaceManager().initialize(cms); // fire "clear caches" event to reload all cached resource bundles OpenCms.fireCmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, new HashMap<String, Object>()); // following changes will not be published right now, switch back to previous project cms.getRequestContext().setCurrentProject(currentProject); // change resource types and schema locations if (isTrue(m_cloneInfo.getChangeResourceTypes())) { changeResourceTypes(resTypeMap); } // adjust container pages CmsObject cloneCms = OpenCms.initCmsObject(cms); if (isTrue(m_cloneInfo.getApplyChangesEverywhere())) { cloneCms.getRequestContext().setSiteRoot("/"); } if (m_cloneInfo.isRewriteContainerPages()) { CmsResourceFilter f = CmsResourceFilter .requireType(CmsResourceTypeXmlContainerPage.getContainerPageTypeId()); List<CmsResource> allContainerPages = cloneCms.readResources("/", f); replacePath(sourceModulePath, targetModulePath, allContainerPages); } } catch (Throwable e) { LOG.error(e.getLocalizedMessage(), e); getReport().addError(e); } finally { cms.getRequestContext().setCurrentProject(currentProject); } } /** * Returns <code>true</code> if the module has been created successful.<p> * * @return <code>true</code> if the module has been created successful */ public boolean success() { return OpenCms.getModuleManager().getModule(m_cloneInfo.getName()) != null; } /** * Adjusts the module configuration file and the formatter configurations.<p> * * @param targetModule the target module * @param resTypeMap the resource type mapping * * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException if the file content could not be read with the determined encoding */ private void adjustConfigs(CmsModule targetModule, Map<I_CmsResourceType, I_CmsResourceType> resTypeMap) throws CmsException, UnsupportedEncodingException { String modPath = CmsWorkplace.VFS_PATH_MODULES + targetModule.getName() + "/"; CmsObject cms = getCms(); if (((m_cloneInfo.getSourceNamePrefix() != null) && (m_cloneInfo.getTargetNamePrefix() != null)) || !m_cloneInfo.getSourceNamePrefix().equals(m_cloneInfo.getTargetNamePrefix())) { // replace resource type names in formatter configurations List<CmsResource> resources = cms.readResources(modPath, CmsResourceFilter.requireType(OpenCms .getResourceManager().getResourceType(CmsFormatterConfigurationCache.TYPE_FORMATTER_CONFIG))); String source = "<Type><!\\[CDATA\\[" + m_cloneInfo.getSourceNamePrefix(); String target = "<Type><!\\[CDATA\\[" + m_cloneInfo.getTargetNamePrefix(); Function<String, String> replaceType = new ReplaceAll(source, target); for (CmsResource resource : resources) { transformResource(resource, replaceType); } resources.clear(); } // replace resource type names in module configuration try { CmsResource config = cms.readResource(modPath + CmsADEManager.CONFIG_FILE_NAME, CmsResourceFilter .requireType(OpenCms.getResourceManager().getResourceType(CmsADEManager.MODULE_CONFIG_TYPE))); Function<String, String> substitution = Functions.identity(); // compose the substitution functions from simple substitution functions for each type for (Map.Entry<I_CmsResourceType, I_CmsResourceType> mapping : resTypeMap.entrySet()) { substitution = Functions.compose( new ReplaceAll(mapping.getKey().getTypeName(), mapping.getValue().getTypeName()), substitution); } // Either replace prefix in or prepend it to the folder name value Function<String, String> replaceFolderName = new ReplaceAll( "(<Folder>[ \n]*<Name><!\\[CDATA\\[)(" + m_cloneInfo.getSourceNamePrefix() + ")?", "$1" + m_cloneInfo.getTargetNamePrefix()); substitution = Functions.compose(replaceFolderName, substitution); transformResource(config, substitution); } catch (CmsVfsResourceNotFoundException e) { LOG.info(e.getLocalizedMessage(), e); } } /** * Adjusts the paths of the module resources from the source path to the target path.<p> * * @param targetResources the paths to adjust * @param sourceModuleName the source module name * @param targetModuleName the target module name * @param sourcePathPart the path part of the source module * @param targetPathPart the path part of the target module * @param iconPaths the path where resource type icons are located * @return the adjusted paths */ private List<String> adjustModuleResourcePaths(List<String> targetResources, String sourceModuleName, String targetModuleName, String sourcePathPart, String targetPathPart, Map<String, String> iconPaths) { List<String> newTargetResources = new ArrayList<String>(); for (String modRes : targetResources) { String nIcon = iconPaths.get(modRes.substring(modRes.lastIndexOf('/') + 1)); if (nIcon != null) { // the referenced resource is an resource type icon, add the new icon path newTargetResources.add(ICON_PATH + nIcon); } else if (modRes.contains(sourceModuleName)) { // there is the name in it newTargetResources.add(modRes.replaceAll(sourceModuleName, targetModuleName)); } else if (modRes.contains(sourcePathPart)) { // there is a path in it newTargetResources.add(modRes.replaceAll(sourcePathPart, targetPathPart)); } else { // there is whether the path nor the name in it newTargetResources.add(modRes); } } return newTargetResources; } /** * Adjusts the paths of the module resources from the source path to the target path.<p> * * @param sourceModule the source module * @param targetModule the target module * @param sourcePathPart the path part of the source module * @param targetPathPart the path part of the target module * @param iconPaths the path where resource type icons are located */ private void adjustModuleResources(CmsModule sourceModule, CmsModule targetModule, String sourcePathPart, String targetPathPart, Map<String, String> iconPaths) { List<String> newTargetResources = adjustModuleResourcePaths(targetModule.getResources(), sourceModule.getName(), targetModule.getName(), sourcePathPart, targetPathPart, iconPaths); targetModule.setResources(newTargetResources); List<String> newTargetExcludeResources = adjustModuleResourcePaths(targetModule.getExcludeResources(), sourceModule.getName(), targetModule.getName(), sourcePathPart, targetPathPart, iconPaths); targetModule.setExcludeResources(newTargetExcludeResources); } /** * Manipulates a string by cutting of a prefix, if present, and adding a new prefix. * * @param word the string to be manipulated * @param oldPrefix the old prefix that should be replaced * @param newPrefix the new prefix that is added * @return the manipulated string */ private String alterPrefix(String word, String oldPrefix, String newPrefix) { if (word.startsWith(oldPrefix)) { return word.replaceFirst(oldPrefix, newPrefix); } return (newPrefix + word); } /** * Changes the resource types and the schema locations of existing content.<p> * * @param resTypeMap a map containing the source types as keys and the target types as values * * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException if the file content could not be read with the determined encoding */ private void changeResourceTypes(Map<I_CmsResourceType, I_CmsResourceType> resTypeMap) throws CmsException, UnsupportedEncodingException { CmsObject cms = getCms(); CmsObject cloneCms = OpenCms.initCmsObject(cms); if (isTrue(m_cloneInfo.getApplyChangesEverywhere())) { cloneCms.getRequestContext().setSiteRoot("/"); } for (Map.Entry<I_CmsResourceType, I_CmsResourceType> mapping : resTypeMap.entrySet()) { CmsResourceFilter filter = CmsResourceFilter.requireType(mapping.getKey()); List<CmsResource> resources = cloneCms.readResources("/", filter); String sourceSchemaPath = mapping.getKey().getConfiguration().get("schema"); String targetSchemaPath = mapping.getValue().getConfiguration().get("schema"); for (CmsResource res : resources) { if (lockResource(cms, res)) { CmsFile file = cms.readFile(res); if (CmsResourceTypeXmlContent.isXmlContent(file)) { CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); xmlContent.setAutoCorrectionEnabled(true); file = xmlContent.correctXmlStructure(getCms()); } String encoding = CmsLocaleManager.getResourceEncoding(cms, file); String content = new String(file.getContents(), encoding); content = content.replaceAll(sourceSchemaPath, targetSchemaPath); file.setContents(content.getBytes(encoding)); try { cms.writeFile(file); } catch (CmsXmlException e) { LOG.error(e.getMessage(), e); } res.setType(mapping.getValue().getTypeId()); cms.writeResource(res); } } } } /** * Copies the explorer type icons.<p> * * @param iconPaths the path to the location where the icons are located * * @throws CmsException if something goes wrong */ private void cloneExplorerTypeIcons(Map<String, String> iconPaths) throws CmsException { for (Map.Entry<String, String> entry : iconPaths.entrySet()) { String source = ICON_PATH + entry.getKey(); String target = ICON_PATH + entry.getValue(); if (getCms().existsResource(source) && !getCms().existsResource(target)) { getCms().copyResource(source, target); } } } /** * Copies the explorer type definitions.<p> * * @param targetModule the target module * @param iconPaths the path to the location where the icons are located * @param descKeys a map that contains a mapping of the explorer type definitions messages */ private void cloneExplorerTypes(CmsModule targetModule, Map<String, String> iconPaths, Map<String, String> descKeys) { List<CmsExplorerTypeSettings> targetExplorerTypes = targetModule.getExplorerTypes(); for (CmsExplorerTypeSettings expSetting : targetExplorerTypes) { descKeys.put(expSetting.getKey(), alterPrefix(expSetting.getKey(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix())); String newIcon = alterPrefix(expSetting.getIcon(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix()); String newBigIcon = alterPrefix(expSetting.getBigIconIfAvailable(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix()); iconPaths.put(expSetting.getIcon(), newIcon); iconPaths.put(expSetting.getBigIconIfAvailable(), newBigIcon); String oldExpTypeName = expSetting.getName(); String newExpTypeName = alterPrefix(oldExpTypeName, m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix()); expSetting.setName(newExpTypeName); String newResourcePage = expSetting.getNewResourcePage(); if (newResourcePage != null) { expSetting.setNewResourcePage(alterPrefix(newResourcePage, m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix())); } expSetting.setKey(expSetting.getKey().replaceFirst(oldExpTypeName, newExpTypeName)); expSetting.setIcon(alterPrefix(expSetting.getIcon(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix())); expSetting.setBigIcon(alterPrefix(expSetting.getBigIconIfAvailable(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix())); expSetting .setNewResourceUri(expSetting.getNewResourceUri().replaceFirst(oldExpTypeName, newExpTypeName)); expSetting.setInfo(expSetting.getInfo().replaceFirst(oldExpTypeName, newExpTypeName)); } } /** * Clones the export points of the module and adjusts its paths.<p> * * @param sourceModule the source module * @param targetModule the target module * @param sourcePathPart the source path part * @param targetPathPart the target path part */ private void cloneExportPoints(CmsModule sourceModule, CmsModule targetModule, String sourcePathPart, String targetPathPart) { for (CmsExportPoint exp : targetModule.getExportPoints()) { if (exp.getUri().contains(sourceModule.getName())) { exp.setUri(exp.getUri().replaceAll(sourceModule.getName(), targetModule.getName())); } if (exp.getUri().contains(sourcePathPart)) { exp.setUri(exp.getUri().replaceAll(sourcePathPart, targetPathPart)); } } } /** * Clones/copies the resource types.<p> * @param sourceModule the source module * @param targetModule the target module * @param sourcePathPart the source path part * @param targetPathPart the target path part * @param keys the map where to put in the messages of the resource type * * @return a map with source resource types as key and the taregt resource types as value */ private Map<I_CmsResourceType, I_CmsResourceType> cloneResourceTypes(CmsModule sourceModule, CmsModule targetModule, String sourcePathPart, String targetPathPart, Map<String, String> keys) { Map<I_CmsResourceType, I_CmsResourceType> resourceTypeMapping = new HashMap<I_CmsResourceType, I_CmsResourceType>(); List<I_CmsResourceType> targetResourceTypes = new ArrayList<I_CmsResourceType>(); for (I_CmsResourceType sourceResType : targetModule.getResourceTypes()) { // get the class name attribute String className = sourceResType.getClassName(); // create the class instance I_CmsResourceType targetResType; try { if (className != null) { className = className.trim(); } int newId = -1; boolean exists = true; do { newId = new Random().nextInt((99999)) + 10000; try { OpenCms.getResourceManager().getResourceType(newId); } catch (CmsLoaderException e) { exists = false; } } while (exists); targetResType = (I_CmsResourceType) Class.forName(className).newInstance(); for (String mapping : sourceResType.getConfiguredMappings()) { targetResType.addMappingType(mapping); } targetResType.setAdjustLinksFolder(sourceResType.getAdjustLinksFolder()); if (targetResType instanceof A_CmsResourceType) { A_CmsResourceType concreteTargetResType = (A_CmsResourceType) targetResType; for (CmsProperty prop : sourceResType.getConfiguredDefaultProperties()) { if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(prop.getValue())) { prop.setStructureValue(prop.getStructureValue() .replaceAll(sourceModule.getName(), targetModule.getName()) .replaceAll(sourcePathPart, targetPathPart)); prop.setResourceValue(prop.getResourceValue() .replaceAll(sourceModule.getName(), targetModule.getName()) .replaceAll(sourcePathPart, targetPathPart)); } concreteTargetResType.addDefaultProperty(prop); } for (CmsConfigurationCopyResource conres : sourceResType.getConfiguredCopyResources()) { concreteTargetResType.addCopyResource(conres.getSource(), conres.getTarget(), conres.getTypeString()); } } for (Map.Entry<String, String> entry : sourceResType.getConfiguration().entrySet()) { targetResType.addConfigurationParameter(entry.getKey(), entry.getValue().replaceAll(sourceModule.getName(), targetModule.getName())); } targetResType.setAdditionalModuleResourceType(true); targetResType .initConfiguration( alterPrefix(sourceResType.getTypeName(), m_cloneInfo.getSourceNamePrefix(), m_cloneInfo.getTargetNamePrefix()), newId + "", sourceResType.getClassName()); keys.put(sourceResType.getTypeName(), targetResType.getTypeName()); targetResourceTypes.add(targetResType); resourceTypeMapping.put(sourceResType, targetResType); } catch (Exception e) { // resource type is unknown, use dummy class to import the module resources targetResType = new CmsResourceTypeUnknown(); // write an error to the log LOG.error(org.opencms.configuration.Messages.get().getBundle().key( org.opencms.configuration.Messages.ERR_UNKNOWN_RESTYPE_CLASS_2, className, targetResType.getClass().getName()), e); } } targetModule.setResourceTypes(targetResourceTypes); return resourceTypeMapping; } /** * Creates the target folder for the module clone.<p> * * @param targetModule the target module * @param sourceClassesPath the source module class path * @param targetBaseClassesPath the 'classes' folder of the target module * * @throws CmsException if something goes wrong */ private void createTargetClassesFolder(CmsModule targetModule, String sourceClassesPath, String targetBaseClassesPath) throws CmsException { StringTokenizer tok = new StringTokenizer(targetModule.getName(), "."); int folderId = CmsResourceTypeFolder.getStaticTypeId(); String targetClassesPath = targetBaseClassesPath; while (tok.hasMoreTokens()) { String folder = tok.nextToken(); targetClassesPath += folder + "/"; if (!getCms().existsResource(targetClassesPath)) { getCms().createResource(targetClassesPath, folderId); } } // move exiting content into new classes sub-folder List<CmsResource> propertyFiles = getCms().readResources(sourceClassesPath, CmsResourceFilter.ALL); for (CmsResource res : propertyFiles) { if (!getCms().existsResource(targetClassesPath + res.getName())) { getCms().copyResource(res.getRootPath(), targetClassesPath + res.getName()); } } } /** * Deletes the temporarily copied classes files.<p> * * @param targetModulePath the target module path * @param sourcePathPart the path part of the source module * @param targetPathPart the target path part * * @throws CmsException if something goes wrong */ private void deleteSourceClassesFolder(String targetModulePath, String sourcePathPart, String targetPathPart) throws CmsException { String sourceFirstFolder = sourcePathPart.substring(0, sourcePathPart.indexOf('/')); String targetFirstFolder = sourcePathPart.substring(0, sourcePathPart.indexOf('/')); if (!sourceFirstFolder.equals(targetFirstFolder)) { getCms().deleteResource(targetModulePath + PATH_CLASSES + sourceFirstFolder, CmsResource.DELETE_PRESERVE_SIBLINGS); return; } String[] targetPathParts = CmsStringUtil.splitAsArray(targetPathPart, '/'); String[] sourcePathParts = CmsStringUtil.splitAsArray(sourcePathPart, '/'); int sourceLength = sourcePathParts.length; int diff = 0; for (int i = 0; i < targetPathParts.length; i++) { if (sourceLength >= i) { if (!targetPathParts[i].equals(sourcePathParts[i])) { diff = i + 1; } } } String topSourceClassesPath = targetModulePath + PATH_CLASSES + sourcePathPart.substring(0, sourcePathPart.indexOf('/')) + "/"; if (diff != 0) { topSourceClassesPath = targetModulePath + PATH_CLASSES; for (int i = 0; i < diff; i++) { topSourceClassesPath += sourcePathParts[i] + "/"; } } getCms().deleteResource(topSourceClassesPath, CmsResource.DELETE_PRESERVE_SIBLINGS); } /** * Returns <code>true</code> if form input is selected, checked, on or yes.<p> * * @param value the value to check * * @return <code>true</code> if form input is selected, checked, on or yes */ private boolean isTrue(String value) { if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value)) { if (Boolean.valueOf(value.toLowerCase()).booleanValue() || value.toLowerCase().equals("on") || value.toLowerCase().equals("yes") || value.toLowerCase().equals("checked") || value.toLowerCase().equals("selected")) { return true; } } return false; } /** * Locks the current resource.<p> * * @param cms the current CmsObject * @param cmsResource the resource to lock * * @return <code>true</code> if the given resource was locked was successfully * * @throws CmsException if some goes wrong */ private boolean lockResource(CmsObject cms, CmsResource cmsResource) throws CmsException { CmsLock lock = cms.getLock(cms.getSitePath(cmsResource)); // check the lock if ((lock != null) && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) && lock.isOwnedInProjectBy( cms.getRequestContext().getCurrentUser(), cms.getRequestContext().getCurrentProject())) { // prove is current lock from current user in current project return true; } else if ((lock != null) && !lock.isUnlocked() && !lock.isOwnedBy(cms.getRequestContext().getCurrentUser())) { // the resource is not locked by the current user, so can not lock it return false; } else if ((lock != null) && !lock.isUnlocked() && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) && !lock.isOwnedInProjectBy(cms.getRequestContext().getCurrentUser(), cms.getRequestContext().getCurrentProject())) { // prove is current lock from current user but not in current project // file is locked by current user but not in current project cms.changeLock(cms.getSitePath(cmsResource)); } else if ((lock != null) && lock.isUnlocked()) { // lock resource from current user in current project cms.lockResource(cms.getSitePath(cmsResource)); } lock = cms.getLock(cms.getSitePath(cmsResource)); if ((lock != null) && lock.isOwnedBy(cms.getRequestContext().getCurrentUser()) && !lock.isOwnedInProjectBy(cms.getRequestContext().getCurrentUser(), cms.getRequestContext().getCurrentProject())) { // resource could not be locked return false; } // resource is locked successfully return true; } /** * Renames the vfs resource bundle files within the target module according to the new module's name.<p> * * @param resources the vfs resource bundle files * @param targetModule the target module * @param name the package name of the source module * * @return a list of all xml vfs bundles within the given module * @throws CmsException if something gows wrong */ private List<CmsResource> renameXmlVfsBundles(List<CmsResource> resources, CmsModule targetModule, String name) throws CmsException { for (CmsResource res : resources) { String newName = res.getName().replaceAll(name, targetModule.getName()); String targetRootPath = CmsResource.getFolderPath(res.getRootPath()) + newName; if (!getCms().existsResource(targetRootPath)) { getCms().moveResource(res.getRootPath(), targetRootPath); } } return resources; } /** * Replaces the referenced formatters within the new XSD files with the new formatter paths.<p> * * @param targetModule the target module * * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException if the file content could not be read with the determined encoding */ private void replaceFormatterPaths(CmsModule targetModule) throws CmsException, UnsupportedEncodingException { CmsResource formatterSourceFolder = getCms() .readResource("/system/modules/" + m_cloneInfo.getFormatterSourceModule() + "/"); CmsResource formatterTargetFolder = getCms() .readResource("/system/modules/" + m_cloneInfo.getFormatterTargetModule() + "/"); for (I_CmsResourceType type : targetModule.getResourceTypes()) { String schemaPath = type.getConfiguration().get("schema"); CmsResource res = getCms().readResource(schemaPath); CmsFile file = getCms().readFile(res); if (CmsResourceTypeXmlContent.isXmlContent(file)) { CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); xmlContent.setAutoCorrectionEnabled(true); file = xmlContent.correctXmlStructure(getCms()); } String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); String content = new String(file.getContents(), encoding); content = content.replaceAll(formatterSourceFolder.getRootPath(), formatterTargetFolder.getRootPath()); file.setContents(content.getBytes(encoding)); getCms().writeFile(file); } } /** * Initializes a thread to find and replace all occurrence of the module's path.<p> * * @throws CmsException in case writing the file fails * @throws UnsupportedEncodingException in case of the wrong encoding */ private void replaceModuleName() throws CmsException, UnsupportedEncodingException { CmsResourceFilter filter = CmsResourceFilter.ALL.addRequireFile().addExcludeState(CmsResource.STATE_DELETED) .addRequireTimerange().addRequireVisible(); List<CmsResource> resources = getCms() .readResources(CmsWorkplace.VFS_PATH_MODULES + m_cloneInfo.getName() + "/", filter); for (CmsResource resource : resources) { CmsFile file = getCms().readFile(resource); if (CmsResourceTypeXmlContent.isXmlContent(file)) { CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); xmlContent.setAutoCorrectionEnabled(true); file = xmlContent.correctXmlStructure(getCms()); } byte[] contents = file.getContents(); String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); String content = new String(contents, encoding); Matcher matcher = Pattern.compile(m_cloneInfo.getSourceModuleName()).matcher(content); if (matcher.find()) { contents = matcher.replaceAll(m_cloneInfo.getName()).getBytes(encoding); if (lockResource(getCms(), file)) { file.setContents(contents); getCms().writeFile(file); } } } } /** * Replaces the paths within all the given resources and removes all UUIDs by an regex.<p> * * @param sourceModulePath the search path * @param targetModulePath the replace path * @param resources the resources * * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException if the file content could not be read with the determined encoding */ private void replacePath(String sourceModulePath, String targetModulePath, List<CmsResource> resources) throws CmsException, UnsupportedEncodingException { for (CmsResource resource : resources) { if (resource.isFile()) { CmsFile file = getCms().readFile(resource); if (CmsResourceTypeXmlContent.isXmlContent(file)) { CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); xmlContent.setAutoCorrectionEnabled(true); file = xmlContent.correctXmlStructure(getCms()); } String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); String oldContent = new String(file.getContents(), encoding); String newContent = oldContent.replaceAll(sourceModulePath, targetModulePath); Matcher matcher = Pattern.compile(CmsUUID.UUID_REGEX).matcher(newContent); newContent = matcher.replaceAll(""); newContent = newContent.replaceAll("<uuid></uuid>", ""); if (!oldContent.equals(newContent)) { file.setContents(newContent.getBytes(encoding)); if (!resource.getRootPath().startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) { if (lockResource(getCms(), resource)) { getCms().writeFile(file); } } else { getCms().writeFile(file); } } } } } /** * Replaces the messages for the given resources.<p> * * @param descKeys the replacement mapping * @param resources the resources to consult * * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException if the file content could not be read with the determined encoding */ private void replacesMessages(Map<String, String> descKeys, List<CmsResource> resources) throws CmsException, UnsupportedEncodingException { for (CmsResource resource : resources) { CmsFile file = getCms().readFile(resource); String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); String content = new String(file.getContents(), encoding); for (Map.Entry<String, String> entry : descKeys.entrySet()) { content = content.replaceAll(entry.getKey(), entry.getValue()); } file.setContents(content.getBytes(encoding)); getCms().writeFile(file); } } /** * Reads a file into a string, applies a transformation to the string, and writes the string back to the file.<p> * * @param resource the resource to transform * @param transformation the transformation to apply * @throws CmsException if something goes wrong * @throws UnsupportedEncodingException in case the encoding is not supported */ private void transformResource(CmsResource resource, Function<String, String> transformation) throws CmsException, UnsupportedEncodingException { CmsFile file = getCms().readFile(resource); if (CmsResourceTypeXmlContent.isXmlContent(file)) { CmsXmlContent xmlContent = CmsXmlContentFactory.unmarshal(getCms(), file); xmlContent.setAutoCorrectionEnabled(true); file = xmlContent.correctXmlStructure(getCms()); } String encoding = CmsLocaleManager.getResourceEncoding(getCms(), file); String content = new String(file.getContents(), encoding); content = transformation.apply(content); file.setContents(content.getBytes(encoding)); lockResource(getCms(), file); getCms().writeFile(file); } }