From 2d33e5694b8777ab8d8a47e73c23eab0d1803f18 Mon Sep 17 00:00:00 2001
From: juanf <juanf@ethz.ch>
Date: Tue, 14 May 2024 14:52:58 +0200
Subject: [PATCH] BIS-1050: Embeded image import form zip

---
 .../v3/executor/importer/ImportExecutor.java  | 55 ++++++++++++++-----
 .../xls/importer/utils/FileServerUtils.java   |  4 +-
 .../1/as/services/xls-import/xls-import.py    |  8 ++-
 3 files changed, 50 insertions(+), 17 deletions(-)

diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/importer/ImportExecutor.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/importer/ImportExecutor.java
index 0389e801e59..51363987d1f 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/importer/ImportExecutor.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/importer/ImportExecutor.java
@@ -25,6 +25,7 @@ import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
+import ch.ethz.sis.openbis.generic.server.xls.importer.utils.FileServerUtils;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.importer.ImportOperation;
@@ -51,6 +52,10 @@ public class ImportExecutor implements IImportExecutor
 
     private static final String DATA_FOLDER_NAME = "data" + ZIP_PATH_SEPARATOR;
 
+    private static final String MISCELLANEOUS_FOLDER_NAME = "miscellaneous" + ZIP_PATH_SEPARATOR;
+
+    private static final String FILE_SERVICES_FOLDER_NAME = MISCELLANEOUS_FOLDER_NAME + "file-service" + ZIP_PATH_SEPARATOR;
+
     private static final String XLS_EXTENSION = "." + "xls";
 
     private static final String XLSX_EXTENSION = "." + "xlsx";
@@ -74,25 +79,24 @@ public class ImportExecutor implements IImportExecutor
                 final Map<String, String> importValues = uncompressedImportData.getImportValues() != null
                         ? uncompressedImportData.getImportValues().stream().collect(Collectors.toMap(ImportValue::getName, ImportValue::getValue))
                         : null;
-                importXls(context, operation, scripts, importValues, uncompressedImportData.getFile());
+                importXls(context, operation, scripts, importValues, uncompressedImportData.getFile(), null);
             } else if (importData instanceof ZipImportData)
             {
                 // ZIP file
+                final Map<String, String> scripts = new HashMap<>();
+                final Map<String, String> importValues = new HashMap<>();
+                byte[] xlsFileContent = null;
 
                 final ZipImportData zipImportData = (ZipImportData) importData;
                 try (final ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(zipImportData.getFile())))
                 {
-                    final Map<String, String> scripts = new HashMap<>();
-                    final Map<String, String> importValues = new HashMap<>();
-                    byte[] xlsFileContent = null;
-
                     ZipEntry entry;
                     while ((entry = zip.getNextEntry()) != null)
                     {
                         final String entryName = entry.getName();
                         if (entry.isDirectory())
                         {
-                            if (!SCRIPTS_FOLDER_NAME.equals(entryName) && !DATA_FOLDER_NAME.equals(entryName))
+                            if (!SCRIPTS_FOLDER_NAME.equals(entryName) && !DATA_FOLDER_NAME.equals(entryName) && !MISCELLANEOUS_FOLDER_NAME.equals(entryName))
                             {
                                 throw UserFailureException.fromTemplate("Illegal directory '%s' is found inside the imported file.", entryName);
                             }
@@ -121,15 +125,16 @@ public class ImportExecutor implements IImportExecutor
                             }
                         }
                     }
+                }
 
-                    if (xlsFileContent != null)
-                    {
-                        importXls(context, operation, scripts, importValues, xlsFileContent);
-                    } else
-                    {
-                        throw UserFailureException.fromTemplate("XLS file not found in the root of the imported ZIP file.");
-                    }
+                if (xlsFileContent != null)
+                {
+                    importXls(context, operation, scripts, importValues, xlsFileContent, zipImportData.getFile());
+                } else
+                {
+                    throw UserFailureException.fromTemplate("XLS file not found in the root of the imported ZIP file.");
                 }
+
             } else
             {
                 throw UserFailureException.fromTemplate("Unknown instance of import data '%s'.",
@@ -142,7 +147,8 @@ public class ImportExecutor implements IImportExecutor
     }
 
     private static void importXls(final IOperationContext context, final ImportOperation operation, final Map<String, String> scripts,
-            final Map<String, String> importValues, final byte[] xlsContent)
+            final Map<String, String> importValues, final byte[] xlsContent, byte[] zipImportDataOrNull)
+            throws IOException
     {
         final IApplicationServerInternalApi applicationServerApi = CommonServiceProvider.getApplicationServerApi();
         final ImportOptions importOptions = operation.getImportOptions();
@@ -158,6 +164,27 @@ public class ImportExecutor implements IImportExecutor
                 importValues, ImportModes.valueOf(importOptions.getMode().name()), importerImportOptions, "DEFAULT");
 
         xlsImport.importXLS(xlsContent);
+        if (zipImportDataOrNull != null)
+        {
+            importZipData(zipImportDataOrNull);
+        }
     }
 
+    public static void importZipData(byte[] zipImportDataOrNull) throws IOException
+    {
+        try (final ZipInputStream zip = new ZipInputStream(new ByteArrayInputStream(zipImportDataOrNull)))
+        {
+            ZipEntry entry;
+            while ((entry = zip.getNextEntry()) != null)
+            {
+                final String filePath = entry.getName();
+                if (!entry.isDirectory() && filePath.startsWith(FILE_SERVICES_FOLDER_NAME))
+                {
+                    String fileServicePath = ZIP_PATH_SEPARATOR + filePath.substring(FILE_SERVICES_FOLDER_NAME.length());
+                    byte[] fileBytes = zip.readAllBytes();
+                    FileServerUtils.write(fileServicePath, fileBytes);
+                }
+            }
+        }
+    }
 }
diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/utils/FileServerUtils.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/utils/FileServerUtils.java
index 32160bd9de0..1784e017998 100644
--- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/utils/FileServerUtils.java
+++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/utils/FileServerUtils.java
@@ -43,6 +43,8 @@ public class FileServerUtils
 
     public static Path write(String filePath, byte[] bytes) throws IOException
     {
-        return Files.write(getFilePath(filePath), bytes);
+        Path filePathAsPath = getFilePath(filePath);
+        Files.createDirectories(filePathAsPath);
+        return Files.write(filePathAsPath, bytes);
     }
 }
diff --git a/ui-admin/src/core-plugins/admin/1/as/services/xls-import/xls-import.py b/ui-admin/src/core-plugins/admin/1/as/services/xls-import/xls-import.py
index 393bb0b98b9..dce1366af7d 100644
--- a/ui-admin/src/core-plugins/admin/1/as/services/xls-import/xls-import.py
+++ b/ui-admin/src/core-plugins/admin/1/as/services/xls-import/xls-import.py
@@ -10,6 +10,7 @@ from java.io import File
 from java.lang import Long
 from java.lang import System
 from java.nio.file import Path
+from ch.ethz.sis.openbis.generic.server.asapi.v3.executor.importer import ImportExecutor
 
 def get_update_mode(parameters):
     update_mode = parameters.get('update_mode', 'FAIL_IF_EXISTS')
@@ -48,6 +49,7 @@ def process(context, parameters):
     if method == "import":
         zip = parameters.get('zip', False)
         temp = None
+        zip_bytes = None
         if zip: # Zip mode uses xls_base64 for all multiple XLS + script files
             zip_bytes = base64.b64decode(parameters.get('xls_base64'))
             temp = File.createTempFile("temp", Long.toString(System.nanoTime()))
@@ -73,13 +75,13 @@ def process(context, parameters):
             xls_base64_string = parameters.get('xls_base64', None)
             if xls_base64_string is not None:
                 parameters.put('xls', [ base64.b64decode(xls_base64_string) ])
-        result = _import(context, parameters)
+        result = _import(context, parameters, zip_bytes)
         if temp is not None:
             FileUtils.deleteDirectory(temp)
     return result
 
 
-def _import(context, parameters):
+def _import(context, parameters, zip_bytes):
     """
         Excel import AS service.
         For extensive documentation of usage and Excel layout,
@@ -120,5 +122,7 @@ def _import(context, parameters):
     xls_byte_arrays = parameters.get('xls', None)
     for xls_byte_array in xls_byte_arrays:
         ids.addAll(importXls.importXLS(xls_byte_array))
+    if zip_bytes is not None:
+        ImportExecutor.importZipData(zip_bytes)
 
     return ids
-- 
GitLab