diff --git a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java index 64209fd33da..9b8d684c0ac 100644 --- a/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java +++ b/amps/ags/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/script/ExportPost.java @@ -2,7 +2,7 @@ * #%L * Alfresco Records Management Module * %% - * Copyright (C) 2005 - 2024 Alfresco Software Limited + * Copyright (C) 2005 - 2025 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -28,9 +28,17 @@ package org.alfresco.module.org_alfresco_module_rm.script; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.io.Serializable; import java.io.StringWriter; +import java.io.Writer; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; @@ -39,10 +47,14 @@ import org.alfresco.repo.exporter.ACPExportPackageHandler; import org.alfresco.repo.web.scripts.content.ContentStreamer; import org.alfresco.repo.web.scripts.content.StreamACP; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.view.ExporterCrawlerParameters; import org.alfresco.service.cmr.view.Location; import org.alfresco.service.namespace.QName; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONException; @@ -53,12 +65,15 @@ import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptResponse; +import lombok.Setter; + /** * Creates an RM specific ACP file of nodes to export then streams it back * to the client. * * @author Gavin Cornwell */ +@Setter public class ExportPost extends StreamACP { /** Logger */ @@ -69,6 +84,12 @@ public class ExportPost extends StreamACP /** Content Streamer */ private ContentStreamer contentStreamer; + protected static final String TEMP_FILE_PREFIX = "export_"; + + protected static final String ZIP_EXTENSION = "zip"; + + protected static final String CSV_EXTENSION = "csv"; + /** * @param contentStreamer */ @@ -87,6 +108,8 @@ public void setContentStreamer(ContentStreamer contentStreamer) public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException { File tempACPFile = null; + File tempCSVFile = null; + File tempZIPFile = null; try { NodeRef[] nodeRefs = null; @@ -146,8 +169,22 @@ public void execute(WebScriptRequest req, WebScriptResponse res) throws IOExcept transferFormat ? ZIP_EXTENSION : ACPExportPackageHandler.ACP_EXTENSION, transferFormat); - // stream the ACP back to the client as an attachment (forcing save as) - contentStreamer.streamContent(req, res, tempACPFile, null, true, tempACPFile.getName(), null); +// // stream the ACP back to the client as an attachment (forcing save as) +// contentStreamer.streamContent(req, res, tempACPFile, null, true, tempACPFile.getName(), null); + + // Map model = dynamicAuthoritiesGet.buildModel(req, res); + // create a CSV file with the same nodeRefs data + tempCSVFile = createCSV(nodeRefs); + + tempZIPFile = createZipFile(tempACPFile, tempCSVFile); + + // Stream the file to the browser + res.setContentType("application/zip"); + res.setHeader("Content-Disposition", "attachment; filename=\"exported_files.zip\""); + res.setHeader("Content-Encoding", "UTF-8"); + + // stream the CSV file back to the client + contentStreamer.streamContent(req, res, tempZIPFile, null, true, tempZIPFile.getName(), null); } catch (IOException ioe) { @@ -182,6 +219,130 @@ public void execute(WebScriptRequest req, WebScriptResponse res) throws IOExcept tempACPFile.delete(); } + + // Try and delete the temporary CSV file + if (tempCSVFile != null) { + if (logger.isDebugEnabled()) { + logger.debug("Deleting temporary CSV file: " + tempCSVFile.getAbsolutePath()); + } + tempCSVFile.delete(); + } + + // Try and delete the temporary ZIP file + if (tempZIPFile != null) { + if (logger.isDebugEnabled()) { + logger.debug("Deleting temporary ZIP file: " + tempZIPFile.getAbsolutePath()); + } + tempZIPFile.delete(); + } + } + } + + private File createZipFile(File tempACPFile, File tempCSVFile) throws IOException + { + File tempZipFile = File.createTempFile("exported_files", "." + ZIP_EXTENSION); + + try (FileOutputStream fos = new FileOutputStream(tempZipFile); + ZipOutputStream zipOut = new ZipOutputStream(fos)) { + + // Add ACP file to the ZIP + addFileToZip(zipOut, tempACPFile, tempACPFile.getName()); + + // Add CSV file to the ZIP + addFileToZip(zipOut, tempCSVFile, tempCSVFile.getName()); + + // Finalize the ZIP archive + zipOut.close(); + + return tempZipFile; + + } catch (IOException e) { + // Handle error while creating zip file + throw new IOException("Error creating zip file", e); } } + + private void addFileToZip(ZipOutputStream zipOut, File file, String fileName) throws IOException + { + try (FileInputStream fis = new FileInputStream(file)) { + ZipEntry zipEntry = new ZipEntry(fileName); + zipOut.putNextEntry(zipEntry); + + byte[] buffer = new byte[1024]; + int length; + while ((length = fis.read(buffer)) > 0) { + zipOut.write(buffer, 0, length); + } + + zipOut.closeEntry(); + } + } + + // Helper method to create a CSV file from nodeRefs or other data + public File createCSV(NodeRef[] nodeRefs) throws IOException + { + // Create temporary file in the system's temp directory + File csvFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, "." + CSV_EXTENSION); + + // Use try-with-resources to automatically close FileWriter and CSVPrinter + try (Writer writer = new FileWriter(csvFile); + CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.builder() + .setHeader("NodeRef", "Node Name", "Description", "Parent Folder","Created On","Modified On","Created By") + .build())) + { + // Loop through each NodeRef and write data + for (NodeRef nodeRef : nodeRefs) + { + ChildAssociationRef primaryParent = nodeService.getPrimaryParent(nodeRef); + NodeRef primaryNodeRef = primaryParent.getParentRef(); + String nodeName = getNodeName(nodeRef); + String description = getDescription(primaryNodeRef); + String parentFolder = getParentFolder(primaryNodeRef); + String createdOn = getCreatedOn(primaryNodeRef); + String modifiedOn = getModifiedOn(primaryNodeRef); + String createdBy = getCreatedBy(primaryNodeRef); + csvPrinter.printRecord(nodeRef.toString(), nodeName, description, parentFolder, createdOn, modifiedOn, createdBy); + } + } + return csvFile; + } + + private String getNodeName(NodeRef nodeRef) + { + String nodeName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + return nodeName != null ? nodeName : "Unknown Node Name"; + } + + private String getDescription(NodeRef nodeRef) + { +// Map properties = nodeService.getProperties(nodeRef); + + String description = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_DESCRIPTION); + return description != null ? description : "No Description"; + } + + private String getParentFolder(NodeRef nodeRef) + { + String parentFolderName = (String) nodeService.getProperty(nodeRef, ContentModel.PARENT_FOLDER); + return parentFolderName != null ? parentFolderName : "No Parent folder"; + } + + private String getCreatedOn(NodeRef nodeRef) + { + String createdOn = (String) nodeService.getProperty(nodeRef, ContentModel.CREATED_ON); + return createdOn != null ? createdOn : "No Creation date"; + } + + private String getModifiedOn(NodeRef nodeRef) + { + String modifiedOn = (String) nodeService.getProperty(nodeRef, ContentModel.MODIFIED_ON); + return modifiedOn != null ? modifiedOn : "No Modified date"; + } + + private String getCreatedBy(NodeRef nodeRef) + { + String createdBy = (String) nodeService.getProperty(nodeRef, ContentModel.CREATED_BY); + return createdBy != null ? createdBy : "Unknown Creater"; + } + } diff --git a/data-model/src/main/java/org/alfresco/model/ContentModel.java b/data-model/src/main/java/org/alfresco/model/ContentModel.java index bfac3dc83b0..3f7ad318892 100644 --- a/data-model/src/main/java/org/alfresco/model/ContentModel.java +++ b/data-model/src/main/java/org/alfresco/model/ContentModel.java @@ -466,5 +466,11 @@ public interface ContentModel static final QName ASPECT_CASCADE_UPDATE = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeUpdate"); static final QName PROP_CASCADE_CRC = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeCRC"); static final QName PROP_CASCADE_TX = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "cascadeTx"); - + + static final QName PARENT_FOLDER = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "parentFolder"); + static final QName CREATED_ON = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "createdOn"); + static final QName CREATED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "createdBy"); + static final QName MODIFIED_ON = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "modifiedOn"); + static final QName MODIFIED_BY = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "modifiedBy"); + }