diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 349ff953ab..6a55930f53 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -230,15 +230,15 @@ - - - - - - - - - + + + + + + + + + @@ -300,9 +300,9 @@ ${mail.header} - - ${mail.from.default} - + + ${mail.from.default} + @@ -344,6 +344,12 @@ + + + + + + @@ -482,22 +488,22 @@ false - - - - - - - - - - - - - - false - - + + + + + + + + + + + + + + false + + diff --git a/config/alfresco/mimetype/mimetype-map.xml b/config/alfresco/mimetype/mimetype-map.xml index 8b25d1972e..9e4926bac2 100644 --- a/config/alfresco/mimetype/mimetype-map.xml +++ b/config/alfresco/mimetype/mimetype-map.xml @@ -292,7 +292,10 @@ z - zip + zip + war + jar + ear dwg diff --git a/source/java/org/alfresco/repo/action/executer/ImporterActionExecuter.java b/source/java/org/alfresco/repo/action/executer/ImporterActionExecuter.java index cdcb3445b3..ac3032f8e9 100644 --- a/source/java/org/alfresco/repo/action/executer/ImporterActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/ImporterActionExecuter.java @@ -24,9 +24,24 @@ */ package org.alfresco.repo.action.executer; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.zip.ZipException; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.content.MimetypeMap; @@ -34,13 +49,21 @@ import org.alfresco.repo.importer.ACPImportPackageHandler; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.model.FileExistsException; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.view.ImporterService; import org.alfresco.service.cmr.view.Location; +import org.alfresco.service.namespace.QName; import org.alfresco.util.TempFileProvider; +import org.apache.tools.zip.ZipEntry; +import org.apache.tools.zip.ZipFile; /** * Importer action executor @@ -53,8 +76,10 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase public static final String PARAM_ENCODING = "encoding"; public static final String PARAM_DESTINATION_FOLDER = "destination"; + private static final int BUFFER_SIZE = 16384; private static final String TEMP_FILE_PREFIX = "alf"; - private static final String TEMP_FILE_SUFFIX = ".acp"; + private static final String TEMP_FILE_SUFFIX_ACP = ".acp"; + private static final String TEMP_FILE_SUFFIX_ZIP = ".zip"; /** * The importer service @@ -71,6 +96,16 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase */ private ContentService contentService; + /** + * The mimetype service + */ + private MimetypeService mimetypeService; + + /** + * The file folder service + */ + private FileFolderService fileFolderService; + /** * Sets the ImporterService to use * @@ -100,6 +135,26 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase { this.contentService = contentService; } + + /** + * Sets the MimetypeService to use + * + * @param mimetypeService The MimetypeService + */ + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + + /** + * Sets the FileFolderService to use + * + * @param fileFolderService The FileFolderService + */ + public void setFileFolderService(FileFolderService fileFolderService) + { + this.fileFolderService = fileFolderService; + } /** * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.repo.ref.NodeRef, org.alfresco.repo.ref.NodeRef) @@ -112,19 +167,20 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase ContentReader reader = this.contentService.getReader(actionedUponNodeRef, ContentModel.PROP_CONTENT); if (reader != null) { + NodeRef importDest = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); if (MimetypeMap.MIMETYPE_ACP.equals(reader.getMimetype())) { + // perform an import of an Alfresco ACP file (special format ZIP structure) File zipFile = null; try { // unfortunately a ZIP file can not be read directly from an input stream so we have to create // a temporary file first - zipFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX); + zipFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX_ACP); reader.getContent(zipFile); - + ACPImportPackageHandler importHandler = new ACPImportPackageHandler(zipFile, (String)ruleAction.getParameterValue(PARAM_ENCODING)); - NodeRef importDest = (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); this.importerService.importView(importHandler, new Location(importDest), null, null); } @@ -137,10 +193,108 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase } } } + else if (MimetypeMap.MIMETYPE_ZIP.equals(reader.getMimetype())) + { + // perform an import of a standard ZIP file + ZipFile zipFile = null; + File tempFile = null; + try + { + tempFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, TEMP_FILE_SUFFIX_ACP); + reader.getContent(tempFile); + // NOTE: This encoding allows us to workaround bug: + // http://bugs.sun.com/bugdatabase/view_bug.do;:WuuT?bug_id=4820807 + zipFile = new ZipFile(tempFile, "Cp437"); + + // build a temp dir name based on the ID of the noderef we are importing + File alfTempDir = TempFileProvider.getTempDir(); + File tempDir = new File(alfTempDir.getPath() + File.separatorChar + actionedUponNodeRef.getId()); + try + { + // TODO: improve this code to directly pipe the zip stream output into the repo objects - + // to remove the need to expand to the filesystem first? + extractFile(zipFile, tempDir.getPath()); + importDirectory(tempDir.getPath(), importDest); + } + finally + { + deleteDir(tempDir); + } + } + catch (IOException ioErr) + { + throw new AlfrescoRuntimeException("Failed to import ZIP file.", ioErr); + } + finally + { + // now the import is done, delete the temporary file + if (tempFile != null) + { + tempFile.delete(); + } + } + } } } } + /** + * Recursively import a directory structure into the specified root node + * + * @param dir The directory of files and folders to import + * @param root The root node to import into + */ + private void importDirectory(String dir, NodeRef root) + { + File topdir = new File(dir); + for (File file : topdir.listFiles()) + { + try + { + if (file.isFile()) + { + String fileName = file.getName(); + + // create content node based on the file name + FileInfo fileInfo = this.fileFolderService.create(root, fileName, ContentModel.TYPE_CONTENT); + NodeRef fileRef = fileInfo.getNodeRef(); + + // add titled aspect for the read/edit properties screens + Map titledProps = new HashMap(1, 1.0f); + titledProps.put(ContentModel.PROP_TITLE, fileName); + this.nodeService.addAspect(fileRef, ContentModel.ASPECT_TITLED, titledProps); + + // push the content of the file into the node + InputStream contentStream = new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE); + ContentWriter writer = this.contentService.getWriter(fileRef, ContentModel.PROP_CONTENT, true); + writer.setMimetype(this.mimetypeService.guessMimetype(fileName)); + writer.putContent(contentStream); + } + else + { + // create a folder based on the folder name + FileInfo folderInfo = this.fileFolderService.create(root, file.getName(), ContentModel.TYPE_FOLDER); + NodeRef folderRef = folderInfo.getNodeRef(); + + // add the uifacets aspect for the read/edit properties screens + this.nodeService.addAspect(folderRef, ApplicationModel.ASPECT_UIFACETS, null); + + importDirectory(file.getPath(), folderRef); + } + } + catch (FileNotFoundException e) + { + // TODO: add failed file info to status message? + throw new AlfrescoRuntimeException("Failed to process ZIP file.", e); + } + catch (FileExistsException e) + { + // TODO: add failed file info to status message? + throw new AlfrescoRuntimeException("Failed to process ZIP file.", e); + } + } + } + /** * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) */ @@ -151,5 +305,80 @@ public class ImporterActionExecuter extends ActionExecuterAbstractBase paramList.add(new ParameterDefinitionImpl(PARAM_ENCODING, DataTypeDefinition.TEXT, true, getParamDisplayLabel(PARAM_ENCODING))); } + + /** + * Extract the file and folder structure of a ZIP file into the specified directory + * + * @param archive The ZIP archive to extract + * @param extractDir The directory to extract into + */ + public static void extractFile(ZipFile archive, String extractDir) + { + String fileName; + String destFileName; + byte[] buffer = new byte[BUFFER_SIZE]; + extractDir = extractDir + File.separator; + try + { + for (Enumeration e = archive.getEntries(); e.hasMoreElements();) + { + ZipEntry entry = (ZipEntry) e.nextElement(); + if (!entry.isDirectory()) + { + fileName = entry.getName(); + fileName = fileName.replace('/', File.separatorChar); + destFileName = extractDir + fileName; + File destFile = new File(destFileName); + String parent = destFile.getParent(); + if (parent != null) + { + File parentFile = new File(parent); + if (!parentFile.exists()) parentFile.mkdirs(); + } + InputStream in = new BufferedInputStream(archive.getInputStream(entry), BUFFER_SIZE); + OutputStream out = new BufferedOutputStream(new FileOutputStream(destFileName), BUFFER_SIZE); + int count; + while ((count = in.read(buffer)) != -1) + { + out.write(buffer, 0, count); + } + in.close(); + out.close(); + } + else + { + File newdir = new File(extractDir + entry.getName()); + newdir.mkdir(); + } + } + } + catch (ZipException e) + { + throw new AlfrescoRuntimeException("Failed to process ZIP file.", e); + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Failed to process ZIP file.", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to process ZIP file.", e); + } + } + /** + * Recursively delete a dir of files and directories + * + * @param dir directory to delete + */ + public static void deleteDir(File dir) + { + File elenco = new File(dir.getPath()); + for (File file : elenco.listFiles()) + { + if (file.isFile()) file.delete(); + else deleteDir(file); + } + dir.delete(); + } } diff --git a/source/java/org/alfresco/repo/template/BasePathResultsMap.java b/source/java/org/alfresco/repo/template/BasePathResultsMap.java index ada739ce32..2ec47f1f8c 100644 --- a/source/java/org/alfresco/repo/template/BasePathResultsMap.java +++ b/source/java/org/alfresco/repo/template/BasePathResultsMap.java @@ -70,7 +70,18 @@ public abstract class BasePathResultsMap extends BaseTemplateMap if (xpath.length() != 0) { if (logger.isDebugEnabled()) - logger.debug("Executing xpath: " + xpath); + { + String out = "Executing xpath: " + xpath; + if (params != null) + { + out += " with params:"; + for (QueryParameterDefinition p : params) + { + out += " " + p.getDefault(); + } + } + logger.debug(out); + } List nodes = this.services.getSearchService().selectNodes( this.parent.getNodeRef(),