Commit 71cbbf1b authored by Carsten  Rose's avatar Carsten Rose
Browse files

F4922 / Excel Import: First successful run with dynamic data 'p:?id=excelexport'

parent 4e80990e
Pipeline #672 passed with stage
in 1 minute and 33 seconds
...@@ -6071,24 +6071,25 @@ Download ...@@ -6071,24 +6071,25 @@ Download
Download offers: Download offers:
* download a single file (any type), * Single file - download a single file (any type),
* concatenate several files (uploaded) and/or web pages (=HTML to PDF) into one PDF output file, * PDF create - one or concatenate several files (uploaded) and/or web pages (=HTML to PDF) into one PDF output file,
* create a ZIP archive, filled with several files ('uploaded' or 'HTML to PDF'-converted). * ZIP archive - filled with several files ('uploaded' or 'HTML to PDF'-converted).
* Excel - created from scratch or fill a template xlsx with database values.
The downloads are SIP protected. Only the current user can use the link to download files. The downloads are SIP protected. Only the current user can use the link to download files.
By using the `_link` columnname: By using the `_link` column name:
* the option `d:...` initiate creating the download link and optional specifies an export filename, * the option `d:...` initiate creating the download link and optional specifies an export filename,
* the optional `M:...` (Mode) specifies the export type (file, pdf, zip), * the optional `M:...` (Mode) specifies the export type (file, pdf, zip, export),
* setting `s:1` is mandatory for the download function, * setting `s:1` is mandatory for the download function,
* the alttext `a:...` specifies a message in the download popup. * the alttext `a:...` specifies a message in the download popup.
By using `_pdf`, `_Pdf`, `_file`, `_File`, `_zip`, `_Zip` as columnname, the options `d`, `m` and `s` By using `_pdf`, `_Pdf`, `_file`, `_File`, `_zip`, `_Zip`, `excel` as columnname, the options `d`, `m` and `s`
will be set by automatically. will be set.
All files will be read by PHP - therefore the directory might be protected against direct web access. This way is the All files will be read by PHP - therefore the directory might be protected against direct web access. This is the
preferred way to offer secure downloads via QFQ. preferred option to offer secure downloads via QFQ.
In case the download needs a persistant URL (no SIP, no user session), a regular In case the download needs a persistant URL (no SIP, no user session), a regular
link, pointing directly to a file, have to be used - the download functionality described here is not appropriate for link, pointing directly to a file, have to be used - the download functionality described here is not appropriate for
...@@ -6103,21 +6104,20 @@ Parameter and (element) sources ...@@ -6103,21 +6104,20 @@ Parameter and (element) sources
* *exportFilename* = <filename for save as> - Name, offered in the 'File save as' browser dialog. Default: 'output.<ext>'. * *exportFilename* = <filename for save as> - Name, offered in the 'File save as' browser dialog. Default: 'output.<ext>'.
If there is no `exportFilename` defined, then the original filename is taken. If there is no `exportFilename` defined, then the original filename is taken (if there is one, else: output...).
The user typically expects meaningful and distinct filenames for different download links. The user typically expects meaningful and distinct file names for different download links.
* *popupMessage*: `a:<text>` - will be displayed in the popup window during download. If the creating/download is fast, the window might disappear quickly. * *popupMessage*: `a:<text>` - will be displayed in the popup window during download. If the creating/download is fast, the window might disappear quickly.
* *mode*: `M:<mode>` * *mode*: `M:<mode>`
* *mode* = <file | pdf | zip> - This parameter is optional and can be skipped in most situations. Mandatory * *mode* = <file | pdf | zip | excel>
for 'zip'.
* If `M:file`, the mime type is derived dynamically from the specified file. In this mode, only one element source * If `M:file`, the mime type is derived dynamically from the specified file. In this mode, only one element source
is allowed per download link (no concatenation). is allowed per download link (no concatenation).
* In case of multiple element sources, only `pdf` or `zip` is supported. * In case of multiple element sources, only `pdf`, `zip` and `excel` (template mode) is supported.
* If `M:zip` is used together with `p:...`, `U:...` or `u:..`, those HTML pages will be converted to PDF. Those files * If `M:zip` is used together with `p:...`, `U:...` or `u:..`, those HTML pages will be converted to PDF. Those files
get generic filenames inside the archive. get generic filenames inside the archive.
* If not specified, the **default** 'Mode' depends on the number of specified element sources (=file or web page): * If not specified, the **default** 'Mode' depends on the number of specified element sources (=file or web page):
...@@ -6125,7 +6125,8 @@ Parameter and (element) sources ...@@ -6125,7 +6125,8 @@ Parameter and (element) sources
* If only one `file` is specifed, the default is `file`. * If only one `file` is specifed, the default is `file`.
* If there is a) a page defined or b) multiple elements, the default is `pdf`. * If there is a) a page defined or b) multiple elements, the default is `pdf`.
* *element sources* - for `M:pdf` or `M:zip`, all of the following three element sources might be specified multiple times. Any combination and order of the three options are allowed. * *element sources* - for `M:pdf` or `M:zip`, all of the following three element sources might be specified multiple times.
Any combination and order of the three options are allowed.
* *file*: `F:<pathFileName>` - relative or absolute pathFileName offered for a) download (single), or to be concatenated * *file*: `F:<pathFileName>` - relative or absolute pathFileName offered for a) download (single), or to be concatenated
in a PDF or ZIP. in a PDF or ZIP.
...@@ -6203,6 +6204,8 @@ Example `_pdf`, `_zip`: :: ...@@ -6203,6 +6204,8 @@ Example `_pdf`, `_zip`: ::
Use the `--print-media-type` as wkhtml option to access the page with media type 'printer'. Depending on the website Use the `--print-media-type` as wkhtml option to access the page with media type 'printer'. Depending on the website
configuration this switches off navigation and background images. configuration this switches off navigation and background images.
Rendering PDF letters Rendering PDF letters
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
...@@ -6312,6 +6315,101 @@ is allowed to access: :: ...@@ -6312,6 +6315,101 @@ is allowed to access: ::
page.10.value = Please access from localhost or log in as 'admin' user. page.10.value = Please access from localhost or log in as 'admin' user.
[global] [global]
.. _excel-export:
Excel export
^^^^^^^^^^^^
'On the fly'-creation of an excel file. The file is build in the moment when the user clicks on the download button.
Mode:
* NEW: The export file will be completely build from scratch.
* TEMPLATE: The export file is based on an earlier saved xlsx file (template). The template is unchanged.
Injecting the data is done in the same way in both modes . The only difference is by specifying a xlsx file which will
be used as an template.
If the export file has to be customized (colors, pictures, headlines, ...), the TEMPLATE mode is the preferred option.
Setup
'''''
* Create a special column name `_excel` (or `_link`) in QFQ/Report. As a source, define a T3 page, which have to deliver
the dynamic content. ::
# New
SELECT CONCAT('d:final.xlsx|M:excel|s:1|t:Excel (new)|p:?id=exceldata') AS _link
# Template
SELECT CONCAT('d:final.xlsx|M:excel|s:1|t:Excel (template)|F:fileadmin/template.xlsx|p:?id=exceldata&arg1=hello') AS _link
* Create a T3 page which delivers the content.
* Disable all HTML header and wrapping code on that page. Typoscript setup: ::
config.disableAllHeaderCode = 1
tt_content.stdWrap >
page >
page = PAGE
[usergroup = *] || [IP = 127.0.0.1,192.168.1.*]
page.10 < styles.content.get
[else]
page.10 = TEXT
page.10.value = access forbidden
[global]
* Use the regular QFQ Report syntax to create some output.
* The newline character ist CHAR(10).
* One option per line.
* Empty will be skipped.
* Lines starting with '#' will be skipped (comments)
* Separate <keyword> and <value> by '='.
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| Keyword | Example | Description |
+=============+================+===================================================================================================+
| 'worksheet' | worksheet=main | Select a worksheet in case the excel file has multiple of them. |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'mode' | mode=insert | Values: insert,overwrite. |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'position' | position=A1 | Default is 'A1'. Use the excel notation. |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'row' | row | Start a new row. |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'str', 's' | s=hello world | Set the given string on the given position. The current position will be shiftet one to the right |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'f' | f==SUM(A5:C6) | Set a formular on the given position. The current position will be shiftet one to the right |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
| 'n' | n=123 | Set number on the given position. The current position will be shiftet one to the right |
+-------------+----------------+---------------------------------------------------------------------------------------------------+
Create a output like this: ::
position=D11
s=Hello
s=World
s=First Line
row
s=Second line
n=123
This fills D11, E11, F11, D12
In Report Syntax: ::
10.sql = SELECT 'position=D11' AS _XLS,
's=Hello' AS _XLS,
's=World' AS _XLS,
's=First Line' AS _XLS,
+ 'row' AS _XLS,
's=Second line' AS _XLS,
'n=123' AS _XLS,
.. _drag_and_drop: .. _drag_and_drop:
Drag and drop Drag and drop
......
...@@ -1294,20 +1294,21 @@ const COLUMN_PAGEH = 'pageh'; ...@@ -1294,20 +1294,21 @@ const COLUMN_PAGEH = 'pageh';
const COLUMN_PAGEI = 'pagei'; const COLUMN_PAGEI = 'pagei';
const COLUMN_PAGEN = 'pagen'; const COLUMN_PAGEN = 'pagen';
const COLUMN_PAGES = 'pages'; const COLUMN_PAGES = 'pages';
const COLUMN_PDF = 'pdf'; const COLUMN_PDF = 'pdf';
const COLUMN_FILE = 'file'; const COLUMN_FILE = 'file';
const COLUMN_ZIP = 'zip'; const COLUMN_ZIP = 'zip';
const COLUMN_MONITOR = 'monitor'; const COLUMN_MONITOR = 'monitor';
const COLUMN_EXCEL = 'excel'; const COLUMN_EXCEL = 'excel';
const COLUMN_NL2BR = 'nl2br'; const COLUMN_NL2BR = 'nl2br';
const COLUMN_HTMLENTITIES = 'htmlentities'; const COLUMN_HTMLENTITIES = 'htmlentities';
const COLUMN_STRIPTAGS = 'striptags'; const COLUMN_STRIPTAGS = 'striptags';
const COLUMN_MIME_TYPE = 'mimeType'; // Will also be used to identify equal named columns in upload record. const COLUMN_MIME_TYPE = 'mimeType'; // Will also be used to identify equal named columns in upload record.
const COLUMN_FILE_SIZE = 'fileSize'; // Will also be used to identify equal named columns in upload record. const COLUMN_FILE_SIZE = 'fileSize'; // Will also be used to identify equal named columns in upload record.
const COLUMN_IMPORT = "import"; const COLUMN_IMPORT = "import";
const COLUMN_EXCEL_STRING = 'XLSstring';
const COLUMN_EXCEL_NUMERIC = 'XLSnumeric';
const COLUMN_EXCEL_PLAIN = 'XLS';
const COLUMN_WRAP_TOKEN = '+'; const COLUMN_WRAP_TOKEN = '+';
...@@ -1349,12 +1350,14 @@ const EXCEL_MODE_INSERT = 'insert'; ...@@ -1349,12 +1350,14 @@ const EXCEL_MODE_INSERT = 'insert';
const EXCEL_MODE_OVERWRITE = 'overwrite'; const EXCEL_MODE_OVERWRITE = 'overwrite';
const EXCEL_POSITION = 'position'; const EXCEL_POSITION = 'position';
const EXCEL_ROW = 'row'; const EXCEL_ROW = 'row';
const EXCEL_STRING = 'string'; const EXCEL_STRING2 = 'str';
const EXCEL_NUMBER = 'number'; const EXCEL_STRING = 's';
const EXCEL_CURRENCY = 'currency'; const EXCEL_FORMULA = 'f';
const EXCEL_DATE = 'date'; const EXCEL_NUMERIC = 'n';
const EXCEL_DATETIME = 'datetime'; const EXCEL_BOOL = 'b';
const EXCEL_TIME = 'time'; const EXCEL_NULL = 'null';
const EXCEL_INLINE = 'inlineStr';
const EXCEL_ERROR = 'e';
const MONITOR_TAIL_DEFAULT = 30; const MONITOR_TAIL_DEFAULT = 30;
const MONITOR_APPEND_DEFAULT = 0; const MONITOR_APPEND_DEFAULT = 0;
......
...@@ -93,8 +93,8 @@ class Excel { ...@@ -93,8 +93,8 @@ class Excel {
$worksheet = $spreadsheet->getActiveSheet(); $worksheet = $spreadsheet->getActiveSheet();
$pos = 'A1'; $pos = 'A1';
if(!OnString::splitExcelPos($pos, $posColumn, $posRow)){ if (!OnString::splitExcelPos($pos, $posColumn, $posRow)) {
throw new downloadException("Invalid cell coordinates: " . $pos, ERROR_EXCEL_INVALID_COORDINATES); throw new downloadException("Invalid cell coordinates: " . $pos, ERROR_EXCEL_INVALID_COORDINATES);
} }
$arr = explode(PHP_EOL, $data); $arr = explode(PHP_EOL, $data);
foreach ($arr as $line) { foreach ($arr as $line) {
...@@ -107,6 +107,8 @@ class Excel { ...@@ -107,6 +107,8 @@ class Excel {
} }
$token = explode(':', $line, 2); $token = explode(':', $line, 2);
$key=$token[0];
$value=$token[1];
switch ($token[0]) { switch ($token[0]) {
case EXCEL_WORKSHEET: case EXCEL_WORKSHEET:
throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED); throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED);
...@@ -120,8 +122,8 @@ class Excel { ...@@ -120,8 +122,8 @@ class Excel {
throw new downloadException("Position argument is empty", ERROR_EXCEL_POSITION_ARGUMENT_EMPTY); throw new downloadException("Position argument is empty", ERROR_EXCEL_POSITION_ARGUMENT_EMPTY);
} }
if(!OnString::splitExcelPos($token[1], $posColumn, $posRow)){ if (!OnString::splitExcelPos($token[1], $posColumn, $posRow)) {
throw new downloadException("Invalid cell coordinates: " . $pos, ERROR_EXCEL_INVALID_COORDINATES); throw new downloadException("Invalid cell coordinates: " . $token[1], ERROR_EXCEL_INVALID_COORDINATES);
} }
break; break;
...@@ -130,16 +132,24 @@ class Excel { ...@@ -130,16 +132,24 @@ class Excel {
break; break;
case EXCEL_STRING: case EXCEL_STRING:
$worksheet->setCellValue($posColumn . $posRow, $token[1]); case EXCEL_STRING2:
case EXCEL_FORMULA:
case EXCEL_NUMERIC:
case EXCEL_BOOL:
case EXCEL_NULL:
case EXCEL_INLINE:
case EXCEL_ERROR:
$spreadsheet->getActiveSheet()
->setCellValueExplicit(
$posColumn . $posRow,
$token[1],
$token[0]
);
$posColumn = $this->nextColumn($posColumn); $posColumn = $this->nextColumn($posColumn);
break; break;
case EXCEL_NUMBER: default:
case EXCEL_CURRENCY: throw new downloadException("Excel Export: unknown token " . $token[0], ERROR_UNKNOWN_TOKEN);
case EXCEL_DATE:
case EXCEL_DATETIME:
case EXCEL_TIME:
throw new downloadException("Not implemented: " . $token[0], ERROR_NOT_IMPLEMENTED);
} }
} }
......
...@@ -786,9 +786,9 @@ class Link { ...@@ -786,9 +786,9 @@ class Link {
switch ($mode) { switch ($mode) {
case DOWNLOAD_MODE_PDF: case DOWNLOAD_MODE_PDF:
case DOWNLOAD_MODE_ZIP: case DOWNLOAD_MODE_ZIP:
case DOWNLOAD_MODE_EXCEL:
break; break;
case DOWNLOAD_MODE_FILE: case DOWNLOAD_MODE_FILE:
case DOWNLOAD_MODE_EXCEL:
if ($cnt > 1) { if ($cnt > 1) {
throw new UserFormException("With 'downloadMode' = 'file' only one element source is allowed.", ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES); throw new UserFormException("With 'downloadMode' = 'file' only one element source is allowed.", ERROR_DOWNLOAD_UNEXPECTED_NUMBER_OF_SOURCES);
} }
......
...@@ -790,6 +790,15 @@ class Report { ...@@ -790,6 +790,15 @@ class Report {
$dataImport = new DataImport($this->db); $dataImport = new DataImport($this->db);
$content = $dataImport->process('typo3conf/ext/qfq/qfq/qfq/sample.xlsx', 'test'); $content = $dataImport->process('typo3conf/ext/qfq/qfq/qfq/sample.xlsx', 'test');
break; break;
case COLUMN_EXCEL_PLAIN:
$content .= $columnValue . PHP_EOL;
break;
case COLUMN_EXCEL_STRING:
$content .= EXCEL_STRING . ':' . $columnValue . PHP_EOL;
break;
case COLUMN_EXCEL_NUMERIC:
$content .= EXCEL_NUMERIC . ':' . $columnValue . PHP_EOL;
break;
case "bullet": case "bullet":
if ($columnValue === '') { if ($columnValue === '') {
...@@ -1179,7 +1188,10 @@ class Report { ...@@ -1179,7 +1188,10 @@ class Report {
return ''; return '';
} }
$columNameToMode = [COLUMN_PDF => DOWNLOAD_MODE_PDF, COLUMN_FILE => DOWNLOAD_MODE_FILE, COLUMN_ZIP => DOWNLOAD_MODE_ZIP, COLUMN_EXCEL => DOWNLOAD_MODE_EXCEL]; $columNameToMode = [COLUMN_PDF => DOWNLOAD_MODE_PDF,
COLUMN_FILE => DOWNLOAD_MODE_FILE,
COLUMN_ZIP => DOWNLOAD_MODE_ZIP,
COLUMN_EXCEL => DOWNLOAD_MODE_EXCEL];
$param = explode('|', $columnValue); $param = explode('|', $columnValue);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment