Commit b98a6f7e authored by Carsten  Rose's avatar Carsten Rose

Merge branch 'F10778UploadZIPandUnpack' into 'develop'

F10778 upload zip and unpack

See merge request !270
parents 9893fe32 fa1a8d01
Pipeline #3552 passed with stages
in 4 minutes and 26 seconds
......@@ -162,7 +162,7 @@ Upload to server, before 'save'
...............................
* If a user open's a file for upload via the browse button, that file is immediately transmitted to the server. The user
will see a turning wheel until the upload finished.
* After successfull upload the 'Browse' button disappears and the filename, plus the delete button, will be displayed (client logic).
* After successfully upload the 'Browse' button disappears and the filename, plus the delete button, will be displayed (client logic).
* The uploaded file will be checked: maxsize, mime type, check script.
* The uploaded file is still temporary. It has been renamed from '[STORE_EXTRA][<uploadSip>][FILES_TMP_NAME]' to
'[STORE_EXTRA][<uploadSip>][FILES_TMP_NAME].cached'.
......
......@@ -586,7 +586,7 @@ The `mode` is given via (in this priority):
Mode
;;;;
* *standard*:
* **standard**:
* The form will behave like defined in the form editor.
* Missing required values will a) be indicated and b) block saving the record.
......@@ -2027,7 +2027,24 @@ FormElement.parameter
* The following attributes are hard coded (can't be changed): `s|M:file|d|F`
* fileSplit, fileDestinationSplit, tableNameSplit: see :ref:`split-pdf-upload`
* *fileUnzip* - If the file is a ZIP file (only then) it will be unzipped. If no directory is given via ``fileUnzip``, the
basedir of ``fileDestination`` is taken, appended by ``unpack``.
If an unzip will be done, for each file of the archive STORE_VAR will be filled (name, path of the extracted file,
mime type, size) and the following will be triggered: *sqlValidate, slaveId, sqlBefore, sqlAfter, sqlInsert, sqlUpdate*.
Example::
fileDestination = fileadmin/file_{{id:R}}.zip
fileUnzip
sqlValidate ={{! SELECT '' FROM (SELECT '') AS fake WHERE '{{mimeType:V}}' LIKE 'application/pdf%' }}
expectRecords=1
messageFail=Unexpected filetype
# Set new
sqlAfter={{INSERT INTO Upload (pathFileName) VALUES '{{filename:V}}' }}
* `fileSplit`, `fileDestinationSplit`, `tableNameSplit`: see :ref:`split-pdf-upload`
* Excel Import: QFQ offers functionality to directly import excel data into the database. This functionality can
optionally be combined with saving the file by using the above parameters like `fileDestination`.
......@@ -2159,8 +2176,8 @@ file type.
* [jpeg] - default: `-density 150 -quality 90`
* *fileDestinationSplit* = `<pathFileName (pattern)>` - Target directory and filename pattern for the created &
split'ed files. Default <fileDestination>.split/split.<nr>.<fileSplit>.
If explicit given, respect that SVG needs a printf style for <nr>, whereas JPEG is numbered automatically. E.g. ::
split'ed files. Default <fileDestination>.split/split.<nr>.<fileSplit>.
If explicit given, respect that SVG needs a printf style for <nr>, whereas JPEG is numbered automatically. E.g. ::
[svg] fileDestinationSplit = fileadmin/protected/{{id:R}}.{{filenameBase:V}}.%02d.svg
[jpeg] fileDestinationSplit = fileadmin/protected/{{id:R}}.{{filenameBase:V}}.jpg
......
.. ==================================================
.. ==================================================
.. ==================================================
.. Header hierarchy
.. ==
.. --
.. ^^
.. ""
.. ;;
.. ,,
..
.. --------------------------------------------used to the update the records specified ------
.. Best Practice T3 reST: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/CheatSheet.html
.. Reference: https://docs.typo3.org/m/typo3/docs-how-to-document/master/en-us/WritingReST/Index.html
.. Italic *italic*
.. Bold **bold**
.. Code ``text``
.. External Links: `Bootstrap <http://getbootstrap.com/>`_
.. Add Images: .. image:: ../Images/a4.jpg
..
..
.. Admonitions
.. .. note:: .. important:: .. tip:: .. warning::
.. Color: (blue) (orange) (green) (red)
..
.. Definition:
.. some text becomes strong (only one line)
.. description has to indented
.. -*- coding: utf-8 -*- with BOM.
.. include:: Includes.txt
.. _links:
Links
-----
The links to issue and the GitHub repository are maintained in the Settings.cfg.
You may want to remove this file if all important links are already handled in
Settings.cfg.
:Packagist:
https://packagist.org/packages/<username>/<extension key>
:TER:
https://typo3.org/extensions/repository/view/<extension key>
:Issues:
https://github.com/<username>/<extension key>/issues
:GitHub Repository:
https://github.com/<username>/<extension key>
:Contact:
`@<username> <https://twitter.com/your-username>`__
......@@ -87,7 +87,6 @@ This documentation is for the TYPO3 extension **qfq**.
ApplicationTest
GeneralTips
Release
Links
License
Sitemap
SearchDocs
......
......@@ -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';
......
......@@ -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
......
......@@ -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';
}
}
......@@ -8,6 +8,7 @@
namespace IMATHUZH\Qfq\Core\Helper;
use IMATHUZH\Qfq\Core\Evaluate;
use IMATHUZH\Qfq\Core\Store\Store;
......@@ -37,7 +38,7 @@ class HelperFormElement {
*/
public static function explodeParameterInArrayElements(array &$elements, $keyName) {
foreach ($elements AS $key => $element) {
foreach ($elements as $key => $element) {
self::explodeParameter($element, $keyName);
$elements[$key] = $element;
}
......@@ -58,7 +59,7 @@ class HelperFormElement {
// Do not add FE_SLAVE_ID - it's necessary to detect if a value is given or not.
$default = [FE_SQL_BEFORE => '', FE_SQL_INSERT => '', FE_SQL_UPDATE => '', FE_SQL_DELETE => '', FE_SQL_AFTER => ''];
foreach ($elements AS $key => $element) {
foreach ($elements as $key => $element) {
$elements[$key][FE_TG_INDEX] = 0;
unset($elements[$key][FE_ADMIN_NOTE]);
// $elements[$key][FE_DATA_REFERENCE] = '';
......@@ -91,7 +92,7 @@ class HelperFormElement {
if (!$flagAllowOverwrite) {
// Check if some of the exploded keys conflict with existing keys
$checkKeys = array_keys($arr);
foreach ($checkKeys AS $checkKey) {
foreach ($checkKeys as $checkKey) {
if (!empty($element[$checkKey])) {
self::$store = Store::getInstance();
self::$store->setVar(SYSTEM_FORM_ELEMENT, Logger::formatFormElementName($element), STORE_SYSTEM);
......@@ -861,5 +862,55 @@ 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 \CodeException
* @throws \DbException
* @throws \UserFormException
* @throws \UserReportException
*/
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.\nSQL Raw: " . $fe[FE_SQL_VALIDATE]])
, ERROR_REPORT_FAILED_ACTION);
}
}
\ No newline at end of file
This diff is collapsed.
Markdown is supported
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