From f4aca34b6e68fb848a182743d15ceef2578ac75d Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 10 Sep 2019 11:06:46 +0800 Subject: [PATCH] IDEMPIERE-3997 Attachment panel : ability to download all attachments as zip file. Base on patch from Nicolas Micoud. --- .../src/org/compiere/model/MAttachment.java | 60 ++++++++++++++++ .../src/org/compiere/tools/FileUtil.java | 66 ++++++++++++++++++ .../adempiere/webui/panel/WAttachment.java | 35 +++++++++- .../theme/default/images/SaveAsZip24.png | Bin 0 -> 1333 bytes 4 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 org.adempiere.ui.zk/theme/default/images/SaveAsZip24.png diff --git a/org.adempiere.base/src/org/compiere/model/MAttachment.java b/org.adempiere.base/src/org/compiere/model/MAttachment.java index 303730f3ff..1bcb950e79 100644 --- a/org.adempiere.base/src/org/compiere/model/MAttachment.java +++ b/org.adempiere.base/src/org/compiere/model/MAttachment.java @@ -20,12 +20,19 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.sql.ResultSet; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Target; +import org.apache.tools.ant.taskdefs.Zip; +import org.compiere.tools.FileUtil; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; @@ -633,4 +640,57 @@ public class MAttachment extends X_AD_Attachment return attachid; } + public File saveAsZip() { + if (getEntryCount() < 1) { + return null; + } + + String name = MTable.get(Env.getCtx(), getAD_Table_ID()).getTableName() + "_" + getRecord_ID(); + + File tempfolder = null; + try { + Path tempPath = Files.createTempDirectory(name); + tempfolder = tempPath.toFile(); + } catch (IOException e1) { + throw new AdempiereException("Unable to create temp folder", e1); + } + + File destZipFile = null; + try { + destZipFile = File.createTempFile(name, ".zip"); + } catch (Throwable e) { + throw new AdempiereException("Unable to create temp file", e); + } + destZipFile.delete(); + + MAttachmentEntry[] entries = getEntries(); + MAttachmentEntry entry = null; + int index = 0; + + for (int i = 0; i < entries.length; i++) { + entry = entries[i]; + index = i; + File destinationFile = new File(tempfolder, entry.getName()); + FileUtil.copy(this, destinationFile, index); + } + + Zip zipper = new Zip(); + zipper.setDestFile(destZipFile); + zipper.setBasedir(tempfolder); + zipper.setUpdate(true); + zipper.setCompress(true); + zipper.setCaseSensitive(false); + zipper.setFilesonly(true); + zipper.setTaskName("zip"); + zipper.setTaskType("zip"); + zipper.setProject(new Project()); + zipper.setOwningTarget(new Target()); + zipper.execute(); + + try { + FileUtil.deleteDirectory(tempfolder); + } catch (IOException e) {} + + return destZipFile; + } } // MAttachment diff --git a/org.adempiere.base/src/org/compiere/tools/FileUtil.java b/org.adempiere.base/src/org/compiere/tools/FileUtil.java index 8f3a4e0d64..d3d0100927 100644 --- a/org.adempiere.base/src/org/compiere/tools/FileUtil.java +++ b/org.adempiere.base/src/org/compiere/tools/FileUtil.java @@ -20,13 +20,21 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.text.SimpleDateFormat; import java.util.Calendar; +import org.adempiere.exceptions.AdempiereException; +import org.compiere.model.MAttachment; import org.compiere.util.Env; import org.compiere.util.Util; @@ -429,6 +437,13 @@ public class FileUtil return localFile; } + /** + * + * @param path + * @return true if deleted + * @throws FileNotFoundException + * @Deprecated + */ public static boolean deleteFolderRecursive(File path) throws FileNotFoundException { if (!path.exists()) throw new FileNotFoundException(path.getAbsolutePath()); @@ -441,4 +456,55 @@ public class FileUtil return ret && path.delete(); } + /** + * copy attachment entry to file + * @param attachment + * @param destinationFile + * @param index + */ + public static void copy(MAttachment attachment, File destinationFile, int index) + { + FileOutputStream destinationFileOutputStream=null; + try { + destinationFile.createNewFile(); + destinationFileOutputStream = new FileOutputStream(destinationFile); + byte[] buffer = attachment.getEntryData(index); + destinationFileOutputStream.write(buffer); + } + catch( java.io.FileNotFoundException f ) { + throw new AdempiereException("File not found exception : " + destinationFile.getName() + " : " + f); + } + catch( java.io.IOException e ) { + throw new AdempiereException("IOException : " + e); + } finally { + try { + if (destinationFileOutputStream != null) + destinationFileOutputStream.close(); + } catch(Exception e) { + throw new AdempiereException("Exception : " + e); + } + } + } + + /** + * delete folder recursively + * @param folder + * @throws IOException + */ + public static void deleteDirectory(File folder) throws IOException { + Path directory = folder.toPath(); + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } } // FileUtil diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WAttachment.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WAttachment.java index c1ffc36d21..7965f00fdc 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WAttachment.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/WAttachment.java @@ -17,6 +17,7 @@ package org.adempiere.webui.panel; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; @@ -25,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; import org.adempiere.util.Callback; import org.adempiere.webui.AdempiereWebUI; import org.adempiere.webui.ClientInfo; @@ -44,6 +46,7 @@ import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.FDialog; import org.compiere.model.MAttachment; import org.compiere.model.MAttachmentEntry; +import org.compiere.model.MTable; import org.compiere.util.CLogger; import org.compiere.util.Env; import org.compiere.util.Msg; @@ -76,7 +79,7 @@ public class WAttachment extends Window implements EventListener /** * */ - private static final long serialVersionUID = 4311076973993361653L; + private static final long serialVersionUID = 8266807399792500541L; private static CLogger log = CLogger.getCLogger(WAttachment.class); @@ -99,6 +102,7 @@ public class WAttachment extends Window implements EventListener private Button bDelete = ButtonFactory.createNamedButton(ConfirmPanel.A_DELETE, false, true); private Button bSave = new Button(); + private Button bSaveAllAsZip = new Button(); private Button bDeleteAll = new Button(); private Button bLoad = new Button(); private Button bCancel = ButtonFactory.createNamedButton(ConfirmPanel.A_CANCEL, false, true); @@ -259,6 +263,7 @@ public class WAttachment extends Window implements EventListener toolBar.appendChild(bLoad); toolBar.appendChild(bDelete); toolBar.appendChild(bSave); + toolBar.appendChild(bSaveAllAsZip); toolBar.appendChild(cbContent); toolBar.appendChild(sizeLabel); @@ -281,6 +286,15 @@ public class WAttachment extends Window implements EventListener bSave.setTooltiptext(Msg.getMsg(Env.getCtx(), "AttachmentSave")); bSave.addEventListener(Events.ON_CLICK, this); + bSaveAllAsZip.setEnabled(false); + bSaveAllAsZip.setSclass("img-btn"); + if (ThemeManager.isUseFontIconForImage()) + bSaveAllAsZip.setIconSclass("z-icon-file-zip-o"); + else + bSaveAllAsZip.setImage(ThemeManager.getThemeResource("images/SaveAsZip24.png")); + bSaveAllAsZip.setTooltiptext(Msg.getMsg(Env.getCtx(), "ExportZIP")); + bSaveAllAsZip.addEventListener(Events.ON_CLICK, this); + if (ThemeManager.isUseFontIconForImage()) bLoad.setIconSclass("z-icon-Import"); else @@ -420,6 +434,7 @@ public class WAttachment extends Window implements EventListener sizeLabel.setText(size.toPlainString() + unit); bSave.setEnabled(true); + bSaveAllAsZip.setEnabled(true); bDelete.setEnabled(true); if (autoPreviewList.contains(mimeType)) @@ -436,6 +451,7 @@ public class WAttachment extends Window implements EventListener else { bSave.setEnabled(false); + bSaveAllAsZip.setEnabled(false); bDelete.setEnabled(false); sizeLabel.setText(""); return false; @@ -572,6 +588,8 @@ public class WAttachment extends Window implements EventListener saveAttachmentToFile(); } else if (e.getTarget() == bRefresh) { displayData(cbContent.getSelectedIndex(), true); + } else if (e.getTarget() == bSaveAllAsZip) { + saveAllAsZip(); } } // onEvent @@ -734,4 +752,19 @@ public class WAttachment extends Window implements EventListener } return "UTF-8"; } + + private void saveAllAsZip() { + File zipFile = m_attachment.saveAsZip(); + + if (zipFile != null) { + String name = MTable.get(Env.getCtx(), m_attachment.getAD_Table_ID()).getTableName() + "_" + m_attachment.getRecord_ID(); + AMedia media = null; + try { + media = new AMedia(name, null, "application/zip", zipFile, true); + } catch (Exception e) { + throw new AdempiereException("Error when converting zip file to media : " + e); + } + Filedownload.save(media); + } + } } diff --git a/org.adempiere.ui.zk/theme/default/images/SaveAsZip24.png b/org.adempiere.ui.zk/theme/default/images/SaveAsZip24.png new file mode 100644 index 0000000000000000000000000000000000000000..d29dbd5186067bb10e6e90b7f12bedb72928ef8e GIT binary patch literal 1333 zcmV-51Qp_niCgdvD&1G*)7_xUHJt5?bQ~H;|Ba zVdy5-u22f4&|)p(O;=smTZ01u@3OCn7lFh!v|RI)}Np5(!Q zzlf^37oQLUd7dF^xpCtL^YinZe(^>8#2c#;$K&zb)vH(EId|^d;_~wH`_4HF_&tBS zSEYy;fP44vlcgy!#(fA7DbCrQDBTGhG5M$^b>EhCqM~`-JE@3jUtZ!`K5=WM|C?_Rq6GV*ck0)PEU{NcmRn#g* z40+L_-|I2h++=QU4iUjxOOhmn5b)mPy(fgKA^b@Rnu@Kph$yO$uS*iFwQOx|v9hv4 zyWPPUgQ}7wmL##b#G$IwIMafs@%KJ7L8lc+ErG-)EG(R5cBV^Nj!9EDbuvKEn#zOW zzm6icjarmWdxlT@YxGuEacR!_#s+sj`WxNZE(WRX)M9PIz6J1A1k(>w8P*EQQpb3_ zOK)wBhj%|_Zmvt3rl<%a2HT*0%>36;)NrS^);`qp2jE=7U~@pX+hw>lM792}D%0mZ z+gkolL{M9}G^ZItLTD1CDI0?UWzM6fxBWm+#g$!V><#h15weE8Qs&M8$@LNy^q5Vd5j zf>3UQ5$^2_nUuREX$Q?Z7!xVS72kRG2%O zUCw8J|09(j6GO%P{L5(4Vtw^b3_kr3>k7n9ur4E--My~X#O%GqC>T-lEG5q~s_iu% z+>$C=~yG3lHe00000NkvXXu0mjf0eY9> literal 0 HcmV?d00001