From 1ca7697ad215529e42a90ead591c6ec1370a049b Mon Sep 17 00:00:00 2001
From: enured <enis.nuredini@uzh.ch>
Date: Tue, 6 Dec 2022 13:20:59 +0100
Subject: [PATCH] F15111: Implemented new special column name _saveZip. New
 feature to generate and save zip with given sources. refs #15111

---
 Documentation/Report.rst                   | 29 ++++++++++++++++++++++
 extension/Classes/Core/Constants.php       |  3 +++
 extension/Classes/Core/Report/Download.php | 17 ++++++++++---
 extension/Classes/Core/Report/Report.php   | 19 ++++++++++----
 4 files changed, 60 insertions(+), 8 deletions(-)

diff --git a/Documentation/Report.rst b/Documentation/Report.rst
index 5d40a5800..8a5c7845a 100644
--- a/Documentation/Report.rst
+++ b/Documentation/Report.rst
@@ -639,6 +639,8 @@ Summary:
 +------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | _savePdf               | :ref:`column-save-pdf` - pre render PDF files                                                                                                                                               |
 +------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| _saveZip               | :ref:`column-save-zip` - Generate and save zip file.                                                                                                                                        |
++------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | _excel                 | :ref:`excel-export` - creates Excel exports based on QFQ Report queries, optional with pre uploaded Excel template files                                                                    |
 +------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
 | _yank                  | :ref:`copyToClipboard`. Shortcut version of the link interface                                                                                                                              |
@@ -1569,6 +1571,33 @@ Example::
   SELECT "d:fileadmin/result.pdf|F:fileadmin/_temp_/test.pdf|U:id=test&--orientation=landscape" AS _savePdf
 
 
+.. _column-save-zip:
+
+Column: _saveZip
+^^^^^^^^^^^^^^^^
+
+Generated ZIP can be stored directly on the server with this functionality. The link query consists of the following parameters:
+
+* One or more element sources (such as ``F:``, ``U:``, ``p:``, see :ref:`download-parameter-files`), including possible wkhtmltopdf parameters
+* Any element sources given with ``F:`` is accepted for insert in zip file.
+* Element sources given with ``U:`` or ``p:`` will first generated as pdf files before they are added to zip. Generated pdf files will be named "file-#.pdf".
+* The export filename and path as ``d:`` - for security reasons, this path has to start with *fileadmin/* and end with *.zip*.
+
+Tips:
+
+* Please note that this option does not render anything in the front end, but is executed each time it is parsed.
+  You may want to add a check to prevent multiple execution.
+* It is not advised to generate the filename with user input for security reasons.
+* If the target file already exists it will be overwriten. To save individual files, choose a new filename,
+  for example by adding a timestamp.
+
+Example::
+
+  SELECT "d:fileadmin/result.zip|F:fileadmin/_temp_/test.pdf" AS _saveZip
+  SELECT "d:fileadmin/result.zip|F:fileadmin/_temp_/test.pdf|U:id=test&--orientation=landscape" AS _saveZip
+  SELECT "d:fileadmin/result.zip|F:fileadmin/_temp_/test.pdf|F:fileadmin/_temp_/test2.xlsx|U:id=test&--orientation=landscape" AS _saveZip
+
+
 .. _column-thumbnail:
 
 Column: _thumbnail
diff --git a/extension/Classes/Core/Constants.php b/extension/Classes/Core/Constants.php
index edb255cf8..99157da3e 100644
--- a/extension/Classes/Core/Constants.php
+++ b/extension/Classes/Core/Constants.php
@@ -247,6 +247,7 @@ const ERROR_TOO_MANY_PARAMETER = 1409;
 const ERROR_INVALID_SAVE_PDF_FILENAME = 1410;
 const ERROR_TWIG_COLUMN_NOT_UNIQUE = 1411;
 const ERROR_DOUBLE_DEFINITION = 1412;
+const ERROR_INVALID_SAVE_ZIP_FILENAME = 1413;
 
 // Upload
 const ERROR_UPLOAD = 1500;
@@ -1729,6 +1730,7 @@ const COLUMN_YANK = 'yank';
 
 const COLUMN_PDF = 'pdf';
 const COLUMN_SAVE_PDF = 'savePdf';
+const COLUMN_SAVE_ZIP = 'saveZip';
 const COLUMN_FILE = 'file';
 const COLUMN_ZIP = 'zip';
 const COLUMN_MONITOR = 'monitor';
@@ -1790,6 +1792,7 @@ const DOWNLOAD_MODE_FILE = 'file';
 const DOWNLOAD_MODE_PDF = 'pdf';
 const DOWNLOAD_MODE_QFQPDF = 'qfqpdf';
 const DOWNLOAD_MODE_SAVE_PDF = 'save-pdf';
+const DOWNLOAD_MODE_SAVE_ZIP = 'save-zip';
 const DOWNLOAD_MODE_EXCEL = 'excel';
 const DOWNLOAD_MODE_ZIP = 'zip';
 const DOWNLOAD_MODE_THUMBNAIL = 'thumbnail';
diff --git a/extension/Classes/Core/Report/Download.php b/extension/Classes/Core/Report/Download.php
index f84b9a2d8..f04565ee0 100644
--- a/extension/Classes/Core/Report/Download.php
+++ b/extension/Classes/Core/Report/Download.php
@@ -529,6 +529,11 @@ class Download {
             case TOKEN_URL_PARAM:
             case TOKEN_PAGE:
             case TOKEN_UID:
+                // Fake download mode pdf if saveZip is used
+                if ($downloadMode === DOWNLOAD_MODE_SAVE_ZIP) {
+                    $downloadMode = DOWNLOAD_MODE_PDF;
+                }
+
                 $urlParam = OnString::splitParam($value, $rcArgs, $rcSipEncode);
                 $urlParamString = KeyValueStringParser::unparse($urlParam, '=', '&');
                 if ($rcSipEncode) {
@@ -584,7 +589,7 @@ class Download {
      * @return string ZIP filename - has to be deleted later.
      * @throws \DownloadException
      */
-    private function zipFiles(array $files) {
+    private function zipFiles(array $files, $downloadMode = DOWNLOAD_MODE_ZIP) {
 
         $zipFile = HelperFile::tempnam();
         if (false === $zipFile) {
@@ -599,6 +604,11 @@ class Download {
 
         $len = strlen(TMP_FILE_PREFIX);
         $ii = 1;
+        $extension = '';
+        // If download mode saveZip is given, then set extension for these on the fly generated pdfs
+        if ($downloadMode === DOWNLOAD_MODE_SAVE_ZIP) {
+            $extension = '.pdf';
+        }
         foreach ($files as $item) {
             // a) $item: fileadmin/dog.jpg, b) $item: fileamdmin/doc.jpg:directory1/directory2/dog.jpg
             $arr = explode(PARAM_TOKEN_DELIMITER, $item);
@@ -608,7 +618,7 @@ class Download {
 
             // If the final filename is a QFQ tempfile and still contains TMP_FILE_PREFIX: remove the prefix and replace it by 'file-?'
             if (substr($localName, 0, $len) == TMP_FILE_PREFIX) {
-                $localName = 'file-' . $ii;
+                $localName = 'file-' . $ii . $extension;
                 $ii++;
             }
 
@@ -740,7 +750,8 @@ class Download {
         // Export, Concat File(s)
         switch ($downloadMode) {
             case DOWNLOAD_MODE_ZIP:
-                $filename = $this->zipFiles($srcFiles);
+            case DOWNLOAD_MODE_SAVE_ZIP:
+                $filename = $this->zipFiles($srcFiles, $downloadMode);
                 if (empty($vars[DOWNLOAD_EXPORT_FILENAME])) {
                     $vars[DOWNLOAD_EXPORT_FILENAME] = basename($filename);
                 }
diff --git a/extension/Classes/Core/Report/Report.php b/extension/Classes/Core/Report/Report.php
index 3d515622b..a2fcc6f13 100644
--- a/extension/Classes/Core/Report/Report.php
+++ b/extension/Classes/Core/Report/Report.php
@@ -1142,21 +1142,30 @@ class Report {
                     $content .= $this->link->renderLink($linkValue);
                     break;
 
+                case COLUMN_SAVE_ZIP:
                 case COLUMN_SAVE_PDF:
                     $tokenGiven = [];
                     $vars = $this->link->fillParameter(array(), $columnValue, $tokenGiven);
-                    $vars[DOWNLOAD_MODE] = DOWNLOAD_MODE_PDF;
+                    if ($columnName === COLUMN_SAVE_ZIP) {
+                        $vars[DOWNLOAD_MODE] = DOWNLOAD_MODE_SAVE_ZIP;
+                        $extension = '.zip';
+                        $errorCode = ERROR_INVALID_SAVE_ZIP_FILENAME;
+                    } else {
+                        $vars[DOWNLOAD_MODE] = DOWNLOAD_MODE_PDF;
+                        $extension = '.pdf';
+                        $errorCode = ERROR_INVALID_SAVE_PDF_FILENAME;
+                    }
                     $vars[SIP_DOWNLOAD_PARAMETER] = implode(PARAM_DELIMITER, $vars[NAME_COLLECT_ELEMENTS]);
 
-                    // Save file with specified export filename
+                    // Save file with specified export5715 filename
                     $pathFileName = $vars[DOWNLOAD_EXPORT_FILENAME];
                     $sanitizedFileName = Sanitize::safeFilename($pathFileName, false, true);
                     if ($pathFileName == '' ||
                         substr($pathFileName, 0, strlen("fileadmin/")) !== "fileadmin/" ||
-                        substr($pathFileName, -4) !== '.pdf') {
-                        throw new \UserReportException("savePdf filenames need to be in the fileadmin/ directory and end in .pdf for security reasons.", ERROR_INVALID_SAVE_PDF_FILENAME);
+                        substr($pathFileName, -4) !== $extension) {
+                        throw new \UserReportException("'$columnName' filenames need to be in the fileadmin/ directory and end in '$extension' for security reasons.", $errorCode);
                     } elseif ($pathFileName !== $sanitizedFileName) {
-                        throw new \UserReportException("The provided filename '$pathFileName' does not meet sanitize criteria. Use '$sanitizedFileName' instead.", ERROR_INVALID_SAVE_PDF_FILENAME);
+                        throw new \UserReportException("The provided filename '$pathFileName' does not meet sanitize criteria. Use '$sanitizedFileName' instead.", $errorCode);
                     } else {
                         $vars[DOWNLOAD_EXPORT_FILENAME] = $sanitizedFileName;
                         $download = new Download();
-- 
GitLab