diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php
index 3e03467c418cc6af3bd5f5178e8c56b0cce78cec..2f39901ea860341a78ac596416996e96d1f8efc0 100644
--- a/extension/Classes/Core/Constants.php
+++ b/extension/Classes/Core/Constants.php
@@ -242,7 +242,7 @@ const ERROR_STORE_KEY_EXIST = 1201;
 
 // I/O Error
 const ERROR_IO_COPY = 1300;
-
+const ERROR_IO_ZIP_OPEN = 1301;
 const ERROR_IO_RMDIR = 1302;
 const ERROR_IO_WRITE = 1303;
 const ERROR_IO_OPEN = 1304;
@@ -1129,6 +1129,8 @@ const FE_FILE_REPLACE_MODE = 'fileReplace'; // Flag if a) QFQ throw an error if
 const FE_FILE_REPLACE_MODE_ALWAYS = 'always'; // Value for flag FE_FILE_REPLACE_MODE
 const FE_FILE_MIME_TYPE_ACCEPT = 'accept'; // Comma separated list of mime types
 const FE_FILE_MAX_FILE_SIZE = SYSTEM_FILE_MAX_FILE_SIZE; // Max upload file size
+const FE_FILE_UNZIP = 'fileUnzip'; // 0|1|dir|{{SELECT ...}}
+const FE_FILE_UNPACK_DIR = 'unpack'; // default dir if not specified
 
 const FE_FILE_CAPTURE = 'capture'; // On a smartphone opens the camera
 const FE_FILE_SPLIT = 'fileSplit';
diff --git a/extension/Classes/Core/Form/FormAction.php b/extension/Classes/Core/Form/FormAction.php
index c452db09b1dd668f508d9893a9117e2650bfbd01..aed3b24c53451c714668b113f9f7ef3116139232 100644
--- a/extension/Classes/Core/Form/FormAction.php
+++ b/extension/Classes/Core/Form/FormAction.php
@@ -200,7 +200,7 @@ class FormAction {
                 $this->store->setStore($arr, STORE_LDAP, true);
             }
 
-            $this->sqlValidate($fe);
+            HelperFormElement::sqlValidate($this->evaluate, $fe);
 
             if ($fe[FE_TYPE] === FE_TYPE_SENDMAIL) {
                 $this->doSendMail($fe);
@@ -291,57 +291,6 @@ class FormAction {
         $sendMail->process($mailConfig);
     }
 
-    /**
-     * If there is a query defined in fe.parameter.FE_SQL_VALIDATE: fire them.
-     * Count the selected records and compare them with fe.parameter.FE_EXPECT_RECORDS.
-     * If match: everything is fine, do nothing.
-     * Else throw \UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
-     *
-     * @param array $fe
-     *
-     * @throws \CodeException
-     * @throws \DbException
-     * @throws \UserFormException
-     * @throws \UserReportException
-     */
-    private function sqlValidate(array $fe) {
-
-        // Is there something to check?
-        if ($fe[FE_SQL_VALIDATE] === '') {
-            return;
-        }
-
-        if ($fe[FE_EXPECT_RECORDS] === '') {
-            throw new \UserFormException("Missing parameter '" . FE_EXPECT_RECORDS . "'", ERROR_MISSING_EXPECT_RECORDS);
-        }
-        $expect = $this->evaluate->parse($fe[FE_EXPECT_RECORDS]);
-
-        if ($fe[FE_MESSAGE_FAIL] === '') {
-            throw new \UserFormException("Missing parameter '" . FE_MESSAGE_FAIL . "'", ERROR_MISSING_MESSAGE_FAIL);
-        }
-
-        // Do the check
-        $result = $this->evaluate->parse($fe[FE_SQL_VALIDATE], ROW_REGULAR);
-        if (!is_array($result)) {
-            throw new \UserFormException("Expected an array for '" . FE_SQL_VALIDATE . "', got a scalar. Please check for {{!...", ERROR_EXPECTED_ARRAY);
-        }
-
-        // If there is at least one record count given, who matches: return 'check succeeded'
-        $countRecordsArr = explode(',', $expect);
-        foreach ($countRecordsArr as $count) {
-            if (count($result) == $count) {
-                return; // check successfully passed
-            }
-        }
-
-        $msg = $this->evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
-
-        // Throw user error message
-        throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => $msg
-            , ERROR_MESSAGE_TO_DEVELOPER => 'validate() failed']), ERROR_REPORT_FAILED_ACTION);
-
-    }
-
     /**
      * Process slaveId, sqlBefore, sqlInsert|sqlUpdate|sqlDelete, sqlAfter.
      * flagFeAction=false: for Native Elements
diff --git a/extension/Classes/Core/Helper/HelperFile.php b/extension/Classes/Core/Helper/HelperFile.php
index 5619f9c0c6cf2510d4c215ad5adcbd673690e00d..a9b1935276d6c9d7bcfc8286bd722dc84045514a 100644
--- a/extension/Classes/Core/Helper/HelperFile.php
+++ b/extension/Classes/Core/Helper/HelperFile.php
@@ -101,6 +101,8 @@ class HelperFile {
 
     /**
      * Returns an array with filestat information to $pathFileName
+     * - mimeType
+     * - fileSize
      *
      * @param $pathFileName
      * @return array
@@ -540,5 +542,43 @@ class HelperFile {
 
         return $pre . $separator . $post;
     }
+
+    /**
+     * Translates ZIP error codes to text.
+     *
+     * @param $errno
+     * @return string
+     */
+    public static function zipFileErrMsg($errno) {
+
+        // using constant name as a string to make this function PHP4 compatible
+        $zipFileFunctionsErrors = array(
+            'ZIPARCHIVE::ER_MULTIDISK' => 'Multi-disk zip archives not supported.',
+            'ZIPARCHIVE::ER_RENAME' => 'Renaming temporary file failed.',
+            'ZIPARCHIVE::ER_CLOSE' => 'Closing zip archive failed',
+            'ZIPARCHIVE::ER_SEEK' => 'Seek error',
+            'ZIPARCHIVE::ER_READ' => 'Read error',
+            'ZIPARCHIVE::ER_WRITE' => 'Write error',
+            'ZIPARCHIVE::ER_CRC' => 'CRC error',
+            'ZIPARCHIVE::ER_ZIPCLOSED' => 'Containing zip archive was closed',
+            'ZIPARCHIVE::ER_NOENT' => 'No such file.',
+            'ZIPARCHIVE::ER_EXISTS' => 'File already exists',
+            'ZIPARCHIVE::ER_OPEN' => 'Can\'t open file',
+            'ZIPARCHIVE::ER_TMPOPEN' => 'Failure to create temporary file.',
+            'ZIPARCHIVE::ER_ZLIB' => 'Zlib error',
+            'ZIPARCHIVE::ER_MEMORY' => 'Memory allocation failure',
+            'ZIPARCHIVE::ER_CHANGED' => 'Entry has been changed',
+            'ZIPARCHIVE::ER_COMPNOTSUPP' => 'Compression method not supported.',
+            'ZIPARCHIVE::ER_EOF' => 'Premature EOF',
+            'ZIPARCHIVE::ER_INVAL' => 'Invalid argument',
+            'ZIPARCHIVE::ER_NOZIP' => 'Not a zip archive',
+            'ZIPARCHIVE::ER_INTERNAL' => 'Internal error',
+            'ZIPARCHIVE::ER_INCONS' => 'Zip archive inconsistent',
+            'ZIPARCHIVE::ER_REMOVE' => 'Can\'t remove file',
+            'ZIPARCHIVE::ER_DELETED' => 'Entry has been deleted',
+        );
+
+        return $zipFileFunctionsErrors[$errno] ?? 'unknown';
+    }
 }
 
diff --git a/extension/Classes/Core/Helper/HelperFormElement.php b/extension/Classes/Core/Helper/HelperFormElement.php
index 33c8dcd1575de89af0d55dc7d3ee38d24737dd3d..cb467d8f0d1d79743c60548d08e685116f480387 100644
--- a/extension/Classes/Core/Helper/HelperFormElement.php
+++ b/extension/Classes/Core/Helper/HelperFormElement.php
@@ -9,6 +9,7 @@
 namespace IMATHUZH\Qfq\Core\Helper;
 
 use IMATHUZH\Qfq\Core\Store\Store;
+use IMATHUZH\Qfq\Core\Evaluate;
 
 
 /**
@@ -861,5 +862,51 @@ EOF;
         return '<div class="help-block with-errors hidden"></div>';
     }
 
+    /**
+     * If there is a query defined in fe.parameter.FE_SQL_VALIDATE: fire them.
+     * Count the selected records and compare them with fe.parameter.FE_EXPECT_RECORDS.
+     * If match: everything is fine, do nothing.
+     * Else throw \UserFormException with error message of fe.parameter.FE_MESSAGE_FAIL
+     *
+     * @param array $fe
+     * @param Evaluate $evaluate
+     * @throws \UserFormException
+     */
+    public static function sqlValidate(Evaluate $evaluate, array $fe) {
 
+        // Is there something to check?
+        if ($fe[FE_SQL_VALIDATE] === '') {
+            return;
+        }
+
+        if ($fe[FE_EXPECT_RECORDS] === '') {
+            throw new \UserFormException("Missing parameter '" . FE_EXPECT_RECORDS . "'", ERROR_MISSING_EXPECT_RECORDS);
+        }
+        $expect = $evaluate->parse($fe[FE_EXPECT_RECORDS]);
+
+        if ($fe[FE_MESSAGE_FAIL] === '') {
+            throw new \UserFormException("Missing parameter '" . FE_MESSAGE_FAIL . "'", ERROR_MISSING_MESSAGE_FAIL);
+        }
+
+        // Do the check
+        $result = $evaluate->parse($fe[FE_SQL_VALIDATE], ROW_REGULAR);
+        if (!is_array($result)) {
+            throw new \UserFormException("Expected an array for '" . FE_SQL_VALIDATE . "', got a scalar. Please check for {{!...", ERROR_EXPECTED_ARRAY);
+        }
+
+        // If there is at least one record count given, who matches: return 'check succeeded'
+        $countRecordsArr = explode(',', $expect);
+        foreach ($countRecordsArr as $count) {
+            if (count($result) == $count) {
+                return; // check successfully passed
+            }
+        }
+
+        $msg = $evaluate->parse($fe[FE_MESSAGE_FAIL]); // Replace possible dynamic parts
+
+        // Throw user error message
+        throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => $msg
+            , ERROR_MESSAGE_TO_DEVELOPER => 'validate() failed']), ERROR_REPORT_FAILED_ACTION);
+
+    }
 }
\ No newline at end of file
diff --git a/extension/Classes/Core/Save.php b/extension/Classes/Core/Save.php
index 886444cae4b6274f6ee793ced7195c907f2799be..5f98928f6a6da49346788964a5b7c48c3d3dd5e7 100644
--- a/extension/Classes/Core/Save.php
+++ b/extension/Classes/Core/Save.php
@@ -20,6 +20,7 @@ use IMATHUZH\Qfq\Core\Helper\Support;
 use IMATHUZH\Qfq\Core\Store\FillStoreForm;
 use IMATHUZH\Qfq\Core\Store\Sip;
 use IMATHUZH\Qfq\Core\Store\Store;
+use ZipArchive;
 
 /**
  * Class Save
@@ -254,7 +255,7 @@ class Save {
         $formValues = $this->createEmptyTemplateGroupElements($formValues);
 
         // Iterate over all table.columns. Built an assoc array $newValues.
-        foreach ($tableColumns AS $column) {
+        foreach ($tableColumns as $column) {
 
             // Never save a predefined 'id': autoincrement values will be given by database..
             if ($column === COLUMN_ID) {
@@ -408,7 +409,7 @@ class Save {
      */
     private function isColumnUploadField($feName) {
 
-        foreach ($this->feSpecNative AS $formElement) {
+        foreach ($this->feSpecNative as $formElement) {
             if ($formElement[FE_NAME] === $feName && $formElement[FE_TYPE] == FE_TYPE_UPLOAD)
                 return true;
         }
@@ -501,12 +502,22 @@ class Save {
 
         $sip = new Sip(false);
         $newValues = array();
-        $vars = array();
+
+        $flagDoUnzip = false;
 
         $formValues = $this->store->getStore(STORE_FORM);
         $primaryRecord = $this->store->getStore(STORE_RECORD); // necessary to check if the current formElement exist as a column of the primary table.
 
-        foreach ($this->feSpecNative AS $formElement) {
+        // Upload - Take care the necessary target directories exist.
+        $cwd = getcwd();
+        $sitePath = $this->store->getVar(SYSTEM_SITE_PATH, STORE_SYSTEM);
+        if ($cwd === false || $sitePath === false || !HelperFile::chdir($sitePath)) {
+            throw new \UserFormException(
+                json_encode([ERROR_MESSAGE_TO_USER => 'getcwd() failed or SITE_PATH undefined or chdir() failed', ERROR_MESSAGE_TO_DEVELOPER => "getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed."]),
+                ERROR_IO_CHDIR);
+        }
+
+        foreach ($this->feSpecNative as $formElement) {
             // skip non upload formElements
             if ($formElement[FE_TYPE] != FE_TYPE_UPLOAD) {
                 continue;
@@ -523,7 +534,32 @@ class Save {
             }
 
             $column = $formElement[FE_NAME];
+
+            $statusUpload = $this->store->getVar($formValues[$column] ?? '', STORE_EXTRA);
+            // Get file stats
+            $vars = array();
+            $vars[VAR_FILE_SIZE] = $statusUpload[FILES_SIZE];
+            $vars[VAR_FILE_MIME_TYPE] = $statusUpload[FILES_TYPE];
+
+            // Check for 'unzip'.
+            if (isset($formElement[FE_FILE_UNZIP]) && $formElement[FE_FILE_UNZIP] != '0' && $vars[VAR_FILE_MIME_TYPE] = 'application/zip') {
+                $flagDoUnzip = true;
+            }
+
+            // Do upload
             $pathFileName = $this->doUpload($formElement, ($formValues[$column] ?? ''), $sip, $modeUpload);
+            if ($flagDoUnzip) {
+                if ($formElement[FE_FILE_UNZIP] == '' || $formElement[FE_FILE_UNZIP] == '1') {
+                    // Set default dir.
+                    $formElement[FE_FILE_UNZIP] = HelperFile::joinPathFilename(dirname($pathFileName), FE_FILE_UNPACK_DIR);
+                }
+
+                // Backup STORE_VAR - will be changed in doUnzip()
+                $tmpStoreVar = $this->store->getStore(STORE_VAR);
+                $this->doUnzip($formElement, $pathFileName);
+                // Restore STORE_VAR
+                $this->store->setStore($tmpStoreVar, STORE_VAR, true);
+            }
 
             if ($modeUpload == UPLOAD_MODE_DELETEOLD && $pathFileName == '') {
                 $pathFileNameTmp = '';  // see '4'
@@ -540,15 +576,15 @@ class Save {
                 // No new upload and no existing: take care to remove previous upload file statistics.
                 $this->store->unsetVar(VAR_FILE_MIME_TYPE, STORE_VAR);
                 $this->store->unsetVar(VAR_FILE_SIZE, STORE_VAR);
-                $vars[VAR_FILE_SIZE] = 0;
-                $vars[VAR_FILE_MIME_TYPE] = '';
             } else {
-                $vars = HelperFile::getFileStat($pathFileNameTmp);
+
                 $this->store->appendToStore($vars, STORE_VAR);
             }
 
             // If given: fire a sqlBefore query
-            $this->evaluate->parse($formElement[FE_SQL_BEFORE]);
+            if (!$flagDoUnzip) {
+                $this->evaluate->parse($formElement[FE_SQL_BEFORE]);
+            }
 
             // Upload Type: Simple or Advanced
             // If (isset($primaryRecord[$column])) { - see #5048 - isset does not deal correctly with NULL!
@@ -567,22 +603,97 @@ class Save {
                 }
             } elseif (isset($formElement[FE_IMPORT_TO_TABLE]) && !isset($formElement[FE_SLAVE_ID])) {
                 // Excel import on nonexisting column -> no upload
+            } elseif ($flagDoUnzip) {
+                // If ZIP and advanced upload: process it not here but via doUnzip.
             } else {
                 // 'Advanced Upload'
                 $this->doUploadSlave($formElement, $modeUpload);
             }
 
             // If given: fire a sqlAfter query
-            $this->evaluate->parse($formElement[FE_SQL_AFTER]);
-
+            if (!$flagDoUnzip) {
+                $this->evaluate->parse($formElement[FE_SQL_AFTER]);
+            }
         }
 
+        // Clean up
+        HelperFile::chdir($cwd);
+
         // Only used in 'Simple Upload'
         if (count($newValues) > 0) {
             $this->updateRecord($this->formSpec[F_TABLE_NAME], $newValues, $recordId, $this->formSpec[F_PRIMARY_KEY]);
         }
     }
 
+    /**
+     * Unzip $pathFileName to $formElement[FE_FILE_UNZIP]. Before final extract, fire FE_SQL_VALIDATE.
+     * For each file in ZIP:
+     * - Fill STORE_VAR with VAR_FILENAME, VAR_FILENAME_ONLY, VAR_FILENAME_BASE, VAR_FILENAME_EXT, VAR_FILE_MIME_TYPE, VAR_FILE_SIZE.
+     * - Fire $formElement[FE_SQL_VALIDATE]
+     * - Fire FE_SLAVE_ID, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, FE_SQL_AFTER
+     *
+     * @param array $formElement
+     * @param string $pathFileName
+     * @throws \CodeException
+     * @throws \DbException
+     * @throws \UserFormException
+     * @throws \UserReportException
+     */
+    private function doUnzip(array $formElement, $pathFileName) {
+
+        if (!is_readable($pathFileName)) {
+            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Open ZIP file failed",
+                ERROR_MESSAGE_TO_DEVELOPER => "File: " . $pathFileName]),
+                ERROR_IO_ZIP_OPEN);
+        }
+
+        $zip = new ZipArchive();
+        $res = $zip->open($pathFileName);
+        if ($res !== true) {
+            throw new \UserFormException(json_encode([ERROR_MESSAGE_TO_USER => "Open ZIP file failed" . HelperFile::zipFileErrMsg($res),
+                ERROR_MESSAGE_TO_DEVELOPER => "File: " . $pathFileName]), ERROR_IO_ZIP_OPEN);
+        }
+
+        // Do sqlValidate() before final extraction.
+        if (!empty($formElement[FE_SQL_VALIDATE])) {
+            for ($i = 0; $i < $zip->numFiles; $i++) {
+                $stat = $zip->statIndex($i);
+
+                $itemPathFileName = HelperFile::joinPathFilename($formElement[FE_FILE_UNZIP], $stat['name']);
+                $this->store->appendToStore(HelperFile::getFileStat($itemPathFileName), STORE_VAR);
+                $this->store->appendToStore(HelperFile::pathinfo($itemPathFileName), STORE_VAR);
+
+                HelperFormElement::sqlValidate($this->evaluate, $formElement);
+            }
+        }
+
+        // Extract
+        if (false === $zip->extractTo($formElement[FE_FILE_UNZIP])) {
+            throw new \UserFormException("Failed to extract ZIP.", ERROR_IO_ZIP_OPEN);
+        }
+        // Close Zip
+        if (false === $zip->close()) {
+            throw new \UserFormException("Failed to close ZIP.", ERROR_IO_ZIP_OPEN);
+        }
+
+        // Process
+        if (!empty($formElement[FE_SLAVE_ID] . $formElement[FE_SQL_BEFORE] . $formElement[FE_SQL_INSERT] .
+            $formElement[FE_SQL_UPDATE] . $formElement[FE_SQL_DELETE] . $formElement[FE_SQL_AFTER])) {
+            for ($i = 0; $i < $zip->numFiles; $i++) {
+                $stat = $zip->statIndex($i);
+
+                $itemPathFileName = HelperFile::joinPathFilename($formElement[FE_FILE_UNZIP], $stat['name']);
+                $this->store->appendToStore(HelperFile::getFileStat($itemPathFileName), STORE_VAR);
+                $this->store->appendToStore(HelperFile::pathinfo($itemPathFileName), STORE_VAR);
+
+                $this->evaluate->parse($formElement[FE_SQL_BEFORE]);
+                $this->doUploadSlave($formElement, UPLOAD_MODE_NEW);
+                $this->evaluate->parse($formElement[FE_SQL_AFTER]);
+                print_r(basename($stat['name']) . PHP_EOL);
+            }
+        }
+    }
+
     /**
      * Process all Upload FormElements for the given $recordId.
      * After processing, &$formValues will be updated with the final filename.
@@ -592,7 +703,7 @@ class Save {
      */
     public function processAllImageCutFE() {
 
-        foreach ($this->feSpecNative AS $formElement) {
+        foreach ($this->feSpecNative as $formElement) {
             // skip non upload formElements
             if ($formElement[FE_TYPE] != FE_TYPE_IMAGE_CUT) {
                 continue;
@@ -631,7 +742,7 @@ class Save {
 
         $flagAllRequiredGiven = 1;
 
-        foreach ($this->feSpecNative AS $key => $formElement) {
+        foreach ($this->feSpecNative as $key => $formElement) {
 
             // Do not check retype slave FE.
             if (isset($formElement[FE_RETYPE_SOURCE_NAME])) {
@@ -748,26 +859,26 @@ class Save {
      * Process upload for the given Formelement. If necessary, delete a previous uploaded file.
      * Calculate the final path/filename and move the file to the new location.
      *
-     * Check also: doc/CODING.md
+     * Check also: Documentation-develop/CODING.md
      *
      * @param array $formElement FormElement 'upload'
      * @param string $sipUpload SIP
      * @param Sip $sip
      * @param string $modeUpload UPLOAD_MODE_UNCHANGED | UPLOAD_MODE_NEW | UPLOAD_MODE_DELETEOLD |
      *                            UPLOAD_MODE_DELETEOLD_NEW
-     *
      * @return false|string New pathFilename or false on error
      * @throws \CodeException
      * @throws \DbException
-     * @throws \UserFormException
-     * @throws \UserReportException
      * @throws \PhpOffice\PhpSpreadsheet\Exception
      * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
+     * @throws \UserFormException
+     * @throws \UserReportException
      * @internal param $recordId
      */
     private function doUpload($formElement, $sipUpload, Sip $sip, &$modeUpload) {
         $flagDelete = false;
         $modeUpload = UPLOAD_MODE_UNCHANGED;
+        $pathFileName = '';
 
         // Status information about upload file
         $statusUpload = $this->store->getVar($sipUpload, STORE_EXTRA);
@@ -781,15 +892,6 @@ class Save {
             $this->doImport($formElement, $tmpFile);
         }
 
-        // Upload - Take care the necessary target directories exist.
-        $cwd = getcwd();
-        $sitePath = $this->store->getVar(SYSTEM_SITE_PATH, STORE_SYSTEM);
-        if ($cwd === false || $sitePath === false || !HelperFile::chdir($sitePath)) {
-            throw new \UserFormException(
-                json_encode([ERROR_MESSAGE_TO_USER => 'getcwd() failed or SITE_PATH undefined or chdir() failed', ERROR_MESSAGE_TO_DEVELOPER => "getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed."]),
-                ERROR_IO_CHDIR);
-        }
-
         // Delete existing old file.
         if (isset($statusUpload[FILES_FLAG_DELETE]) && $statusUpload[FILES_FLAG_DELETE] == '1') {
             $arr = $sip->getVarsFromSip($sipUpload);
@@ -819,8 +921,6 @@ class Save {
             Logger::logMessageWithPrefix($msg, $this->qfqLogFilename);
         }
 
-        HelperFile::chdir($cwd);
-
         // Delete current used uniq SIP
         $this->store->setVar($sipUpload, array(), STORE_EXTRA);
 
@@ -828,6 +928,8 @@ class Save {
     }
 
     /**
+     * Excel Import
+     *
      * @param $formElement
      * @param $fileName
      * @throws \CodeException
@@ -977,7 +1079,7 @@ class Save {
             }
 
             // Import the data
-            foreach ($worksheetData AS $rowIndex => $row) {
+            foreach ($worksheetData as $rowIndex => $row) {
                 $columnList = '`' . implode('`,`', $columnListArr) . '`';
                 $paramPlaceholders = str_repeat('?,', count($worksheetData[0]) - 1) . '?';
                 $insertSql = "INSERT INTO `$tableName` ($columnList) VALUES ($paramPlaceholders)";
@@ -989,7 +1091,7 @@ class Save {
     /**
      * Copy uploaded file from temporary location to final location.
      *
-     * Check also: doc/CODING.md
+     * Check also: Documentation-develop/CODING.md
      *
      * @param array $formElement
      * @param array $statusUpload
@@ -1087,9 +1189,9 @@ class Save {
     }
 
     /**
-     * Check's if the file $pathFileName should be splitted in one file per page. If no: do nothing and return.
+     * Check's if the file $pathFileName should be split'ed in one file per PDF page. If no: do nothing and return.
      * The only possible split target file format is 'svg': fileSplit=svg.
-     * The splitted files will be saved under fileDestinationSplit=some/path/to/file.%02d.svg. A printf style token,
+     * The split'ed files will be saved under fileDestinationSplit=some/path/to/file.%02d.svg. A printf style token,
      * like '%02d', is needed to create distinguished filename's. See 'man pdf2svg' for further details.
      * For every created file, a record in table 'Split' is created (see splitSvg() ), storing the pathFileName of the
      * current page/file.
@@ -1221,7 +1323,7 @@ class Save {
      * Create/update or delete the slave record.
      *
      * @param array $fe
-     * @param $modeUpload
+     * @param string $modeUpload UPLOAD_MODE_NEW|UPLOAD_MODE_DELETEOLD_NEW|UPLOAD_MODE_DELETEOLD
      * @return int
      * @throws \CodeException
      * @throws \DbException