Commit 92963fb7 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Refs #11998 - implement central download logic

parent ebae6839
Pipeline #5059 passed with stages
in 3 minutes and 45 seconds
......@@ -1417,7 +1417,7 @@ Run a php function defined in an external script.
* If the function returns an associative array, then the **key-value pairs will be accessible via the VARS store `V`**.
* If the function throws an **exception** then a standard QFQ error message is shown.
* Text sent to 'stderr' by the php function is not returned at all.
* The script has access to the following **qfq functions** using the interface (see examples below):
* The script has access to the following **qfq php functions** using the interface (see examples below):
* $qfq::apiCall($method, $url, $data = '', $header = [], $timeout = 5)
* arguments:
* string $method: can be PUT/POST/GET/DELETE
......@@ -2070,7 +2070,7 @@ By using ``_pdf``, ``_Pdf``, ``_file``, ``_File``, ``_zip``, ``_Zip``, ``_excel
will be set.
All files will be read by PHP - therefore the directory might be protected against direct web access. This is the
preferred option to offer secure downloads via QFQ.
preferred option to offer secure downloads via QFQ. Check `secure-direct-file-access`_.
.. _download-parameter-files:
......@@ -2130,34 +2130,34 @@ Parameter and (element) sources
Link: https://example.com/dl.php/doe/john
* *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 | excel>
* 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).
* 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.
* If not specified, the **default** 'Mode' depends on the number of specified element sources (=file or web page):
* If only one `file` is specified, the default is `file`.
* 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 element sources may be specified multiple times.
* *element sources* - for ``M:pdf`` or ``M:zip``, all of the following element sources may be specified multiple times.
Any combination and order of these 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.
* *page*: `p:id=<t3 page>&<key 1>=<value 1>&<key 2>=<value 2>&...&<key n>=<value n>`.
* *page*: ``p:id=<t3 page>&<key 1>=<value 1>&<key 2>=<value 2>&...&<key n>=<value n>``.
* By default, the options given to wkhtml will *not* be encoded by a SIP!
* To encode the parameter via SIP: Add '_sip=1' to the URL GET parameter.
E.g. `p:id=form&_sip=1&form=Person&r=1`.
E.g. ``p:id=form&_sip=1&form=Person&r=1``.
In that way, specific sources for the `download` might be SIP encrypted.
......@@ -2166,13 +2166,31 @@ Parameter and (element) sources
* If there are trouble with accessing FE_GROUP protected content, please check :ref:`wkhtmltopdf<wkhtml>`.
* *url*: `u:<url>` - any URL, pointing to an internal or external destination.
* *url*: ``u:<url>`` - any URL, pointing to an internal or external destination.
* *uid*: `uid:<tt-content record id>` - the tt_content.uid of a QFQ PageContent record (shown on hover in the backend). This will render
only the specified QFQ content record, without any Typo3 layout elements (Menu, Body,...)
QFQ will retrieve the tt-content's bodytext from the Typo3 database, parse it, and render it as a PDF. Parameters can be
passed: `uid:<tt-content record id>[&arg1=value1][&arg2=value2][...]` and will be available in the SIP store for the QFQ PageContent,
or passed as wkhtmltopdf arguments, if applicable.
* *uid*: ``uid:<function name>`` - the output is treated as HTML (will be converted to PDF) or EXCEL data.
* The called tt-content record is identified by `function name`, specified in the subheader field. Optional
the numeric id of the tt-content record (=uid) can be given.
* Only the specified QFQ content record will be rendered, without any Typo3 layout elements (Menu, Body,...)
* QFQ will retrieve the tt-content's bodytext from the Typo3 database, parse it, and render it as a PDF or Execl data.
* Parameters can be passed: ``uid:<tt-content record id>[&arg1=value1][&arg2=value2][...]`` and will be available via
STORE_SIP in the QFQ PageContent, or passed as wkhtmltopdf arguments, if applicable.
* For more obviously structuring, put the additional tt-content record on the same Typo3 page (where the QFQ
tt-content record is located which produces the link) and specify ``render = api`` (`report-render`_).
* *source*: ``source:<function name>[&arg1=value1][&arg2=value2][&...]`` - (similar to a `uid`) the output is treated
as further sources. Example result reported by *function name* might be: ``F:file.pdf1|uid:myData&arg=2|...``
* Use this functionality to define a *central managed download* function, which can be reused anywhere by just specify the
*function name* and required arguments.
* The called tt-content record is identified by `function name`, specified in the subheader field. Optional
the numeric id of the tt-content record (=uid) can be given.
* The output of the tt-content record will be treated as further source arguments. Nothing else than valid source
references should be printed. Separate the references as usual by '|'.
* The supplied arguments are available via STORE_SIP (this is different from `qfq_function`_).
* Tip: For more obviously structuring, put the additional tt-content record on the same Typo3 page (where the QFQ
tt-content record is located which produces the link) and specify ``render = api`` (`report-render`_).
* *WKHTML Options* for `page`, `urlParam` or `url`:
......@@ -2208,6 +2226,12 @@ Example `_link`: ::
# One source and a header file. Note: the parameter to the header URL is escaped with double backslash.
SELECT "d:complete.pdf|s|t:Complete PDF|p:id=detail2&r=1&--orientation=Landscape&--header={{URL:R}}?indexp.php?id=head\\&L=1|F:fileadmin/pdf/test.pdf" AS _link
# One indirect source reference
SELECT "d:complete.pdf|s|t:Complete PDF|source:centralPdf&pId=1234" AS _link
An additional tt-content record is defined with `sub header: centralPdf`. One or multiple attachments might be concatenated.
10.sql = SELECT '|F:', a.pathFileName FROM Attachments AS a WHERE a.pId={{pId:S}}
..
Example `_pdf`, `_zip`: ::
......
......@@ -1703,6 +1703,7 @@ const NAME_URL = 'url';
const NAME_MAIL = 'mail';
const NAME_PAGE = 'page';
const NAME_UID = 'uid';
const NAME_SOURCE = 'source';
const NAME_TEXT = 'text';
const NAME_DROPDOWN = 'dropdown';
const NAME_DOWNLOAD = DOWNLOAD_EXPORT_FILENAME;
......@@ -1774,6 +1775,7 @@ const TOKEN_URL = 'u';
const TOKEN_MAIL = 'm';
const TOKEN_PAGE = 'p';
const TOKEN_UID = 'uid';
const TOKEN_SOURCE = 'source';
const TOKEN_DOWNLOAD = 'd';
const TOKEN_COPY_TO_CLIPBOARD = 'y';
const TOKEN_DROPDOWN = 'z';
......
......@@ -1116,7 +1116,6 @@ class Database {
* It's important that the current DB class has access to the Typo3 DB.
*
* @param $uid
* @param array $urlParam
* @return array // The full T3 tt-content record.
* @throws \CodeException
* @throws \DbException
......
......@@ -354,8 +354,8 @@ class Download {
}
/**
* @param $uid
* @param $urlParam
* @param string $uid
* @param array $urlParam
* @return string
* @throws \CodeException
* @throws \DbException
......@@ -369,7 +369,7 @@ class Download {
* @throws \UserFormException
* @throws \UserReportException
*/
private function getEvaluatedBodytext($uid, $urlParam) {
private function getEvaluatedBodytext($uid, array $urlParam) {
$bodyTextArr = $this->db->getBodytext($uid);
// Copy $urlParam to STORE_SIP
......@@ -416,7 +416,7 @@ class Download {
$token = $arr[0];
$value = $arr[1];
if ($token === TOKEN_UID) { // extract uid
if ($token === TOKEN_UID || $token === TOKEN_SOURCE) { // extract uid
$uidParamsArr = explode('&', $value, 2);
$uid = $uidParamsArr[0];
$value = $uidParamsArr[1] ?? ''; // additional params
......@@ -514,6 +514,63 @@ class Download {
return $zipFile;
}
/**
* Check $param for 'source:.<function name>&arg1=val1&arg2=val2,....'.
* For each found, expand it by fire the given QFQ function with the arguments.
* Be aware, the result might contain again a 'source:..' definition ... do it recursively.
*
* Returns a string withut any 'source:' definition.
*
* @param string $param
* @return string
* @throws \CodeException
* @throws \DbException
* @throws \DownloadException
* @throws \PhpOffice\PhpSpreadsheet\Exception
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
* @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
* @throws \UserFormException
* @throws \UserReportException
*/
private function checkAndExpandSource($param) {
if ($param == '') {
return '';
}
$final = '';
// $param = 'F:file.pdf|uid:123&pId=22|p:htmlcontent&appId=1|source:myFunction&accId=33'
$elements = explode(PARAM_DELIMITER, $param);
// $elements = [ 'F:file.pdf', 'uid:123&pId=22;, 'p:htmlcontent&appId=1', 'source:myFunction&accId=33' ]
foreach ($elements as $element) {
// E.g.: $element = 'source:myFunction&accId=33' >>
$arr = explode(PARAM_TOKEN_DELIMITER, $element, 2);
// Check for 'source:...' - $arr[0] = 'source'
if (0 === strcmp($arr[0], TOKEN_SOURCE)) {
// $arr[1] = 'myFunction&accId=33&grId=44'
$args = explode('&', $arr[1], 2);
$urlParam = KeyValueStringParser::parse($args[1] ?? '', '=', '&');
// Return a list of
$element = $this->checkAndExpandSource($this->getEvaluatedBodytext($args[0], $urlParam));
if ($element == '') {
continue;
}
}
$final .= '|' . $element;
}
return substr($final, 1);
}
/**
* $vars[DOWNLOAD_EXPORT_FILENAME] - Optional. '<new filename>'
* $vars[DOWNLOAD_MODE] - Optional. file | pdf | excel | thumbnail | monitor - default is a) 'file' in case of only one or b) 'pdf' in case of multiple sources.
......@@ -562,7 +619,8 @@ class Download {
$vars[SIP_DOWNLOAD_PARAMETER] = TOKEN_FILE . ':' . $pathFilenameThumbnail;
}
$elements = explode(PARAM_DELIMITER, $vars[SIP_DOWNLOAD_PARAMETER]);
// Check and expand 'source:...'
$elements = explode(PARAM_DELIMITER, $this->checkAndExpandSource($vars[SIP_DOWNLOAD_PARAMETER]));
// Get all files / content
$tmpData = array();
......
......@@ -147,6 +147,7 @@ class Link {
TOKEN_MAIL => NAME_MAIL,
TOKEN_PAGE => NAME_PAGE,
TOKEN_UID => NAME_UID,
TOKEN_SOURCE => NAME_SOURCE,
TOKEN_DROPDOWN => NAME_DROPDOWN,
TOKEN_DOWNLOAD => NAME_DOWNLOAD,
TOKEN_DOWNLOAD_MODE => NAME_DOWNLOAD_MODE,
......@@ -814,8 +815,8 @@ class Link {
// Store value
if ((isset($rcTokenGiven[TOKEN_DOWNLOAD]) || isset($rcTokenGiven[TOKEN_COPY_TO_CLIPBOARD])) &&
($key == TOKEN_PAGE || $key == TOKEN_URL || $key == TOKEN_URL_PARAM || $key == TOKEN_UID ||
$key == TOKEN_FILE || $key == TOKEN_FILE_DEPRECATED)) {
($key == TOKEN_PAGE || $key == TOKEN_URL || $key == TOKEN_URL_PARAM || $key == TOKEN_UID
|| $key == TOKEN_SOURCE || $key == TOKEN_FILE || $key == TOKEN_FILE_DEPRECATED)) {
$vars[NAME_COLLECT_ELEMENTS][] = $key . ':' . $value;
......
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