Commit fcdaa0f0 authored by Carsten  Rose's avatar Carsten Rose
Browse files

Implements #12085. Add doc and custom error message. Finalize directDownload...

Implements #12085. Add doc and custom error message. Finalize directDownload notation. Add unit test.
parent 1bdb42d6
Pipeline #5032 passed with stages
in 4 minutes and 37 seconds
...@@ -1945,20 +1945,45 @@ Output:: ...@@ -1945,20 +1945,45 @@ Output::
Download Download
-------- --------
Download offers: +--------------------+--------------------------------------------------------+----------------------------------------+
| Mode | Security | Note |
+====================+========================================================+========================================+
| Direct File access | Files are public available. No access restriction | Use ``<a href="...">`` |
| | | Pro: Simple, links can be copied. | | Merge multiple sources: no |
| | | Con: Directory access, guess of filenames, only | but check :ref:`column-save-pdf` |
| | removing the file will deny access. | | Custom 'save as filename': no |
+--------------------+--------------------------------------------------------+----------------------------------------+
| Persistent Link | Access is be defined by a SQL statement. In *T3/BE | Use ``..., 'd:1234|s:0' AS _link`` |
| | > Extension > QFQ > File > download* define a SQL | | Merge multiple sources: yes |
| | statement. | | Custom 'save as filename': yes |
| | | Pro: speaking URL, link can be copied, access can | |
| | can be defined a SQL statement. | |
| | | Con: **Key might be altered by user**, permission | |
| | can't be user logged in dependent. | |
+--------------------+--------------------------------------------------------+----------------------------------------+
| Secure Link | **Default**. SIP protected link. | Use ``..., 'd|F:file.pdf' AS _link`` |
| | | Pro: Parameter can't be altered, most easy definition| | Merge multiple sources: yes |
| | in QFQ, access might be logged in user dependent. | | Custom 'save as filename': yes |
| | | Cons: Links are assigned to a browser session and | |
| | can't be copied | |
+--------------------+--------------------------------------------------------+----------------------------------------+
The rest of this section applies only to `Persistent Link` and `Secure Link`. Download offers:
* Single file - download a single file (any type), * Single file - download a single file (any type),
* PDF create - one or 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,
* 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. * 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.
By using the `_link` column name: 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
* in `SIP` mode: an export filename (),
* in `persistent link` mode: path download script (optional) and key(s) to identify the record with the PathFilename
information (see below).
* the optional `M:...` (Mode) specifies the export type (file, pdf, zip, export), * the optional `M:...` (Mode) specifies the export type (file, pdf, zip, export),
* setting `s:1` is recommended for the download function (file / path name is hidden to the user),
* 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`, `_excel` as column name, the options `d`, `M` and `s` By using `_pdf`, `_Pdf`, `_file`, `_File`, `_zip`, `_Zip`, `_excel` as column name, the options `d`, `M` and `s`
...@@ -1967,16 +1992,12 @@ will be set. ...@@ -1967,16 +1992,12 @@ will be set.
All files will be read by PHP - therefore the directory might be protected against direct web access. This is the 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.
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
such a scenario. If necessary, :ref:`column-save-pdf` can be used to generate such a file.
.. _download-parameter-files: .. _download-parameter-files:
Parameter and (element) sources Parameter and (element) sources
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* *download*: `d[:<exportFilename>]` * mode `secure link` (s:1) - *download*: `d[:<exportFilename>]`
* *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>'.
...@@ -1984,6 +2005,51 @@ Parameter and (element) sources ...@@ -1984,6 +2005,51 @@ Parameter and (element) sources
The user typically expects meaningful and distinct file names for different download links. The user typically expects meaningful and distinct file names for different download links.
* mode `persistent link` (s:0) - *download*: `d:[<path/name>]<key1>[/<keyN>]`
This setup is divided in part a) and b):
Part a) - offering the download link.
* The whole part a) is optional. The download itself will work without it.
* (Optional) *path/name* = of the QFQ `download.php` script. By default``typo3conf/ext/qfq/Classes/Api/download.php``.
Three further possibilities: ``dl.php`` or ``dl2.php`` or ``dl3.php`` (see below).
* *key1* = give a uniq identifier to select the wished record.
Part b) - process the download
* In the QFQ extension config: File > Query for direct download mode: `download.php` or `dl.php` or `dl2.php` or `dl3.php`
up to 4 different SQL statements can be given with the regular QFQ download link syntax (skip the visual elements
like button, text, glyph icon, question,...)::
SELECT CONCAT('d|F:', n.pathFileName) FROM Note AS n WHERE n.id=?
All `?` in the SQL statement will be replaced by the specified parameter. If there are more `?` than parameter,
the last parameter will be reused for all pending `?`.
E.g. ``10.sql = SELECT 'd:1234|t:File.pdf' AS _link`` creates a link
``<a href="typo3conf/ext/qfq/Classes/Api/download.php/1234"><span class="btn btn-default">File.pdf</span></span>``.
If the user clicks on the link, QFQ will extract the `1234` argument and via ``download.php`` the query (defined in
the Typo QFQ extension config) will be prepared and fires ``SELECT CONCAT('d|F:', n.pathFileName, '|t:File.pdf') FROM Note AS n WHERE n.id=1234``.
The download of the file, specified by ``n.pathFileName``, will start.
If no record ist selected, a custom error will be shown. If the query selectes more than one record, a general error will be shown.
If one of ``dl.php`` or ``dl2.php`` or ``dl3.php`` should be used, please initially create the symlink(s), e.g. in the
application directory (same level as typo3conf) ``ln -s typo3conf/ext/qfq/Classes/Api/download.php dl.php`` (or dl2.ph, dl3.php).
Speaking URL)
Instead of using a numeric value reference key, also a text can be used. Always take care that exactly one record is
selected. The key is transferred by URL therefore untrusted: The sanitize class :ref:`alnumx<sanitize-class>` is applied.
Example::
Query: SELECT CONCAT('d|F:', n.pathFileName) FROM Person AS p WHERE p.name=? AND p.firstName=? AND p.publish='yes'
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>`
......
...@@ -684,7 +684,7 @@ class Download { ...@@ -684,7 +684,7 @@ class Download {
* Retrieve SQL query from QFQ config, specific to script name. * Retrieve SQL query from QFQ config, specific to script name.
* Four script names are possible: download.php, dl.php, dl2.php, dl3.php * Four script names are possible: download.php, dl.php, dl2.php, dl3.php
* *
* @return array|bool|int|string|null * @return array // [ 'sql' -> 'SELECT "d|F:file.pdf"', 'error' -> 'Record not found']
* @throws \CodeException * @throws \CodeException
* @throws \DbException * @throws \DbException
* @throws \UserFormException * @throws \UserFormException
...@@ -696,7 +696,16 @@ class Download { ...@@ -696,7 +696,16 @@ class Download {
// Example: /var/www/html/qfq/dl.php >> dl.php // Example: /var/www/html/qfq/dl.php >> dl.php
$scriptName = substr($scriptName, strrpos($scriptName, '/') + 1); $scriptName = substr($scriptName, strrpos($scriptName, '/') + 1);
return $this->store->getVar(SYSTEM_SQL_DIRECT_DOWNLOAD . $scriptName, STORE_SYSTEM . STORE_EMPTY); $arr['sql'] = $this->store->getVar(SYSTEM_SQL_DIRECT_DOWNLOAD . $scriptName, STORE_SYSTEM . STORE_EMPTY);
if ($arr['sql'] == '') {
throw new \DownloadException(json_encode([ERROR_MESSAGE_TO_USER => 'Missing SQL',
ERROR_MESSAGE_TO_DEVELOPER => "Missing SQL in QFQ extension config variable: " . SYSTEM_SQL_DIRECT_DOWNLOAD . $scriptName
])
, ERROR_MISSING_VALUE);
}
$arr['error'] = $this->store->getVar(SYSTEM_SQL_DIRECT_DOWNLOAD . $scriptName . 'error', STORE_SYSTEM . STORE_EMPTY);
return $arr;
} }
/** /**
...@@ -707,10 +716,10 @@ class Download { ...@@ -707,10 +716,10 @@ class Download {
* @throws \UserReportException * @throws \UserReportException
*/ */
private function getDirectDownloadModeDetails() { private function getDirectDownloadModeDetails() {
//TODO was ist wenn sql leer ist
$sql = $this->getDirectDownloadSql();
// Get, Clean: with http://loclhost/qfq/typo3conf/ext/qfq/Classes/Api/download.php/help is $_SERVER['PATH_INFO']='/help'. $arr = $this->getDirectDownloadSql();
// Get, Clean: with http://localhost/qfq/typo3conf/ext/qfq/Classes/Api/download.php/help is $_SERVER['PATH_INFO']='/help'.
$pathInfo = $this->store->getVar('PATH_INFO', STORE_CLIENT . STORE_EMPTY, SANITIZE_ALLOW_ALNUMX); $pathInfo = $this->store->getVar('PATH_INFO', STORE_CLIENT . STORE_EMPTY, SANITIZE_ALLOW_ALNUMX);
$pathInfo = Sanitize::sanitize(urldecode($pathInfo), SANITIZE_ALLOW_ALNUMX); $pathInfo = Sanitize::sanitize(urldecode($pathInfo), SANITIZE_ALLOW_ALNUMX);
...@@ -718,14 +727,17 @@ class Download { ...@@ -718,14 +727,17 @@ class Download {
// In case there are more question mark than parameter, duplicate the last parameter until enough parameter filled. // In case there are more question mark than parameter, duplicate the last parameter until enough parameter filled.
$last = end($param); $last = end($param);
$questionMark = substr_count($sql, '?'); $questionMark = substr_count($arr['sql'], '?');
while ($questionMark > count($param)) { while ($questionMark > count($param)) {
$param[] = $last; $param[] = $last;
} }
//TODO SQL Fehler abfangen: SQL Fehler ausgeben!!
if ($arr['error'] == '') {
$arr['error'] = 'Key for download not found.';
}
// Get cmd which defines the download // Get cmd which defines the download
$param = $this->db->sql($sql, ROW_EXPECT_1, $param); $param = $this->db->sql($arr['sql'], ROW_EXPECT_1, $param, $arr['error']);
// In case there are more than on column: implode // In case there are more than one column: implode
$cmd = implode('', $param); $cmd = implode('', $param);
// Use the link class only to reuse the parsing code of the download element. // Use the link class only to reuse the parsing code of the download element.
$link = new Link(Store::getSipInstance()); $link = new Link(Store::getSipInstance());
......
...@@ -850,12 +850,12 @@ class Link { ...@@ -850,12 +850,12 @@ class Link {
} }
// Download Link needs some extra work // Download Link needs some extra work
if (isset($rcTokenGiven[TOKEN_DOWNLOAD]) && $rcTokenGiven[TOKEN_DOWNLOAD]) { if ($rcTokenGiven[TOKEN_DOWNLOAD] ?? false) {
$vars = $this->buildDownloadLate($vars); $vars = $this->buildDownloadLate($vars);
} }
// CopyToClipboard (Download) Link needs some extra work // CopyToClipboard (Download) Link needs some extra work
if (isset($rcTokenGiven[TOKEN_COPY_TO_CLIPBOARD]) && $rcTokenGiven[TOKEN_COPY_TO_CLIPBOARD]) { if ($rcTokenGiven[TOKEN_COPY_TO_CLIPBOARD] ?? false) {
$vars = $this->buildCopyToClipboardLate($vars); $vars = $this->buildCopyToClipboardLate($vars);
} }
...@@ -985,11 +985,6 @@ class Link { ...@@ -985,11 +985,6 @@ class Link {
case LINK_PICTURE: case LINK_PICTURE:
$countLinkPicture++; $countLinkPicture++;
break; break;
case NAME_FILE:
case NAME_URL:
case NAME_PAGE:
$countSources++;
break;
default: default:
break; break;
} }
...@@ -1008,10 +1003,14 @@ class Link { ...@@ -1008,10 +1003,14 @@ class Link {
throw new \UserReportException ("Token Mail and Target at the same time not possible'" . TOKEN_PAGE . "'", ERROR_MULTIPLE_DEFINITION); throw new \UserReportException ("Token Mail and Target at the same time not possible'" . TOKEN_PAGE . "'", ERROR_MULTIPLE_DEFINITION);
} }
if (isset($tokenGiven[TOKEN_DOWNLOAD]) && count($vars[NAME_COLLECT_ELEMENTS]) == 0 && $countSources == 0) { if (isset($tokenGiven[TOKEN_DOWNLOAD]) && $vars[NAME_SIP] == "1" && count($vars[NAME_COLLECT_ELEMENTS]) == 0) {
throw new \UserReportException ("Missing element sources for download", ERROR_MISSING_REQUIRED_PARAMETER); throw new \UserReportException ("Missing element sources for download", ERROR_MISSING_REQUIRED_PARAMETER);
} }
if (isset($tokenGiven[TOKEN_DOWNLOAD]) && $vars[NAME_SIP] == "0" && count($vars[NAME_COLLECT_ELEMENTS]) > 0) {
throw new \UserReportException ("For persistent downloads, sources are not necessary/allowed in the link definition. Instead, please define the sources in QFQ extension setup > file.", ERROR_MISSING_REQUIRED_PARAMETER);
}
if (isset($tokenGiven[TOKEN_ACTION_DELETE])) { if (isset($tokenGiven[TOKEN_ACTION_DELETE])) {
$this->checkDeleteParam($vars); $this->checkDeleteParam($vars);
} }
...@@ -1107,11 +1106,14 @@ class Link { ...@@ -1107,11 +1106,14 @@ class Link {
$vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TEXT, $altText, $vars[NAME_EXTRA_CONTENT_WRAP]); $vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TEXT, $altText, $vars[NAME_EXTRA_CONTENT_WRAP]);
$vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TITLE, 'Download: ' . addslashes($vars[NAME_DOWNLOAD]), $vars[NAME_EXTRA_CONTENT_WRAP]); $vars[NAME_EXTRA_CONTENT_WRAP] = str_replace(DOWNLOAD_POPUP_REPLACE_TITLE, 'Download: ' . addslashes($vars[NAME_DOWNLOAD]), $vars[NAME_EXTRA_CONTENT_WRAP]);
$tmpUrlParam = array(); if ($vars[NAME_SIP] == '1' || $vars[NAME_SIP] == '') {
$tmpUrlParam[DOWNLOAD_MODE] = $this->getDownloadModeNCheck($vars); // Special encoding only necessary for SIP based downloads.
$tmpUrlParam[DOWNLOAD_EXPORT_FILENAME] = $vars[NAME_DOWNLOAD]; $tmpUrlParam = array();
$tmpUrlParam[SIP_DOWNLOAD_PARAMETER] = base64_encode(implode(PARAM_DELIMITER, $vars[NAME_COLLECT_ELEMENTS])); $tmpUrlParam[DOWNLOAD_MODE] = $this->getDownloadModeNCheck($vars);
$vars[NAME_URL_PARAM] = KeyValueStringParser::unparse($tmpUrlParam, '=', '&'); $tmpUrlParam[DOWNLOAD_EXPORT_FILENAME] = $vars[NAME_DOWNLOAD];
$tmpUrlParam[SIP_DOWNLOAD_PARAMETER] = base64_encode(implode(PARAM_DELIMITER, $vars[NAME_COLLECT_ELEMENTS]));
$vars[NAME_URL_PARAM] = KeyValueStringParser::unparse($tmpUrlParam, '=', '&');
}
} }
// CopyToClipboard // CopyToClipboard
...@@ -1655,6 +1657,8 @@ EOF; ...@@ -1655,6 +1657,8 @@ EOF;
* @param $vars * @param $vars
* @return array * @return array
* @throws \CodeException * @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/ */
private function buildDownloadLate($vars) { private function buildDownloadLate($vars) {
...@@ -1683,6 +1687,11 @@ EOF; ...@@ -1683,6 +1687,11 @@ EOF;
} }
} }
// Download links should be by default SIP encoded.
if (($vars[NAME_SIP]) === false) {
$vars[NAME_SIP] = "1";
}
// No download link! Only text/button // No download link! Only text/button
if ($vars[NAME_RENDER] == '3') { if ($vars[NAME_RENDER] == '3') {
return $vars; return $vars;
...@@ -1703,18 +1712,29 @@ EOF; ...@@ -1703,18 +1712,29 @@ EOF;
$onClick = <<<EOF $onClick = <<<EOF
onclick="$('#qfqModalTitle101').text($(this).data('title')); $('#qfqModalText101').text($(this).data('text'));" onclick="$('#qfqModalTitle101').text($(this).data('title')); $('#qfqModalText101').text($(this).data('text'));"
EOF; EOF;
// Check for SIP encoded download link or persistent download link
$vars[NAME_URL] = Path::appToApi(API_DOWNLOAD_PHP); if ($vars[NAME_SIP] == '1') {
// SIP encoded.
$vars[NAME_URL] = Path::appToApi(API_DOWNLOAD_PHP);
} else {
// Persistent Download Link
if ($vars[NAME_DOWNLOAD] == '') {
throw new \UserReportException("Persistent download link (s:0): please specify target key like 'd:1234'", ERROR_MISSING_VALUE);
}
// Check if there is a shortcut version defined like d:dl.php/123.
if (strstr($vars[NAME_DOWNLOAD], '.php/') === false) {
// Set the default: API_DOWNLOAD_PHP
$vars[NAME_URL] = Path::appToApi(API_DOWNLOAD_PHP) . '/' . $vars[NAME_DOWNLOAD];
} else {
// Take given shortcut
$vars[NAME_URL] = $vars[NAME_DOWNLOAD];
}
}
$vars[NAME_LINK_CLASS_DEFAULT] = NO_CLASS; $vars[NAME_LINK_CLASS_DEFAULT] = NO_CLASS;
$vars[NAME_EXTRA_CONTENT_WRAP] = '<span ' . $attributes . $onClick . '>'; $vars[NAME_EXTRA_CONTENT_WRAP] = '<span ' . $attributes . $onClick . '>';
$vars[NAME_BOOTSTRAP_BUTTON] = '0'; $vars[NAME_BOOTSTRAP_BUTTON] = '0';
// Download links should be by default SIP encoded.
if (($vars[NAME_SIP]) === false) {
$vars[NAME_SIP] = "1";
}
return $vars; return $vars;
} }
......
...@@ -44,6 +44,7 @@ class Session ...@@ -44,6 +44,7 @@ class Session
if (self::$phpUnit === true) { if (self::$phpUnit === true) {
self::$sessionLocal = array(); self::$sessionLocal = array();
} else { } else {
ini_set('session.cookie_httponly', 1); ini_set('session.cookie_httponly', 1);
$lifetime = SYSTEM_COOKIE_LIFETIME; $lifetime = SYSTEM_COOKIE_LIFETIME;
......
...@@ -1506,17 +1506,30 @@ EOF; ...@@ -1506,17 +1506,30 @@ EOF;
$result = $link->renderLink('d'); $result = $link->renderLink('d');
} }
/**
* @expectedException UserReportException
*
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function testDownloadException1() {
$link = new Link($this->sip, DB_INDEX_DEFAULT, true);
// Empty
$result = $link->renderLink('d|F:file.pdf|F:file2.pdf|s:0');
}
/** /**
* @throws \CodeException * @throws \CodeException
* @throws \UserFormException * @throws \UserFormException
* @throws \UserReportException * @throws \UserReportException
*/ */
public function testDownload() { public function testDownloadSecureLink() {
$link = new Link($this->sip, DB_INDEX_DEFAULT, true); $link = new Link($this->sip, DB_INDEX_DEFAULT, true);
// Single file // Single file
$result = $link->renderLink('d|F:file.pdf'); $result = $link->renderLink('d|F:file.pdf');
// $this->assertEquals('<a href="typo3conf/ext/qfq/Classes/Api/download.php?mode=pdf&_exportFilename=&_b64_download=RjpmaWxlLnBkZg==" class="0" title="Download" ><span class="btn btn-default" data-toggle="modal" data-target="#qfqModal101" data-title="Download: " data-text="Please wait" data-backdrop="static" data-keyboard="false" onclick="$(\'#qfqModalTitle101\').text($(this).data(\'title\')); $(\'#qfqModalText101\').text($(this).data(\'text\'));"><span class="glyphicon glyphicon-file" ></span></span></a>', $result);
$this->assertEquals('<a href="typo3conf/ext/qfq/Classes/Api/download.php?s=badcaffee1234" class="0" title="Download" ><span class="btn btn-default" data-toggle="modal" data-target="#qfqModal101" data-title="Download: " data-text="Please wait" data-backdrop="static" data-keyboard="false" onclick="$(\'#qfqModalTitle101\').text($(this).data(\'title\')); $(\'#qfqModalText101\').text($(this).data(\'text\'));"><span class="glyphicon glyphicon-file" ></span></span></a>', $result); $this->assertEquals('<a href="typo3conf/ext/qfq/Classes/Api/download.php?s=badcaffee1234" class="0" title="Download" ><span class="btn btn-default" data-toggle="modal" data-target="#qfqModal101" data-title="Download: " data-text="Please wait" data-backdrop="static" data-keyboard="false" onclick="$(\'#qfqModalTitle101\').text($(this).data(\'title\')); $(\'#qfqModalText101\').text($(this).data(\'text\'));"><span class="glyphicon glyphicon-file" ></span></span></a>', $result);
// With download filename // With download filename
...@@ -1577,6 +1590,25 @@ EOF; ...@@ -1577,6 +1590,25 @@ EOF;
} }
/**
* @throws \CodeException
* @throws \UserFormException
* @throws \UserReportException
*/
public function testDownloadPersistentLink() {
$link = new Link($this->sip, DB_INDEX_DEFAULT, true);
Store::setVar(SYSTEM_SQL_DIRECT_DOWNLOAD . 'downloadphp', 'SELECT "d|F:file.pdf"', STORE_SYSTEM);
$result = $link->renderLink('d:123|s:0');
$this->assertEquals('<a href="typo3conf/ext/qfq/Classes/Api/download.php/123" class="0" title="Download" ><span class="btn btn-default" data-toggle="modal" data-target="#qfqModal101" data-title="Download: 123" data-text="Please wait" data-backdrop="static" data-keyboard="false" onclick="$(\'#qfqModalTitle101\').text($(this).data(\'title\')); $(\'#qfqModalText101\').text($(this).data(\'text\'));"><span class="glyphicon glyphicon-file" ></span></span></a>', $result);
Store::setVar(SYSTEM_SQL_DIRECT_DOWNLOAD . 'dlphp', 'SELECT "d|F:file.pdf"', STORE_SYSTEM);
$result = $link->renderLink('d:dl.php/123|s:0');
$this->assertEquals('<a href="dl.php/123" class="0" title="Download" ><span class="btn btn-default" data-toggle="modal" data-target="#qfqModal101" data-title="Download: dl.php/123" data-text="Please wait" data-backdrop="static" data-keyboard="false" onclick="$(\'#qfqModalTitle101\').text($(this).data(\'title\')); $(\'#qfqModalText101\').text($(this).data(\'text\'));"><span class="glyphicon glyphicon-file" ></span></span></a>', $result);
}
/** /**
* @throws \CodeException * @throws \CodeException
* @throws \UserFormException * @throws \UserFormException
......
...@@ -390,11 +390,24 @@ custom30 = ...@@ -390,11 +390,24 @@ custom30 =
# cat=file/file; type=string; label=Query for direct download mode. Access via download.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire # cat=file/file; type=string; label=Query for direct download mode. Access via download.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire
sqlDirectdownloadphp = sqlDirectdownloadphp =
# cat=file/file; type=string; label=Message shown to the user if record isn't found.
sqlDirectdownloadphperror =
# cat=file/file; type=string; label=Query for direct download mode. Access via dl.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire # cat=file/file; type=string; label=Query for direct download mode. Access via dl.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire
sqlDirectdlphp = sqlDirectdlphp =
# cat=file/file; type=string; label=Message shown to the user if record isn't found.
sqlDirectdlphperror =
# cat=file/file; type=string; label=Query for direct download mode. Access via dl2.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire # cat=file/file; type=string; label=Query for direct download mode. Access via dl2.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire
sqlDirectdl2php = sqlDirectdl2php =
# cat=file/file; type=string; label=Message shown to the user if record isn't found.
sqlDirectdl2phperror =
# cat=file/file; type=string; label=Query for direct download mode. Access via dl3.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire # cat=file/file; type=string; label=Query for direct download mode. Access via dl3.php. No default.: SELECT CONCAT('d:output.pdf|F:', n.pathFileName) FROM notiz AS n WHERE n.id=? AND NOW()<n.expire
sqlDirectdl3php = sqlDirectdl3php =
# cat=file/file; type=string; label=Message shown to the user if record isn't found.
sqlDirectdl3phperror =
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