Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
typo3
qfq
Commits
1260b56e
Commit
1260b56e
authored
Jun 24, 2020
by
Carsten Rose
Browse files
Merge branch 'develop' into 'master'
Develop See merge request
!271
parents
0cf82050
b98a6f7e
Pipeline
#3553
passed with stages
in 4 minutes and 32 seconds
Changes
13
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Documentation-develop/CODING.md
View file @
1260b56e
...
...
@@ -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 successfull
y
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'.
...
...
Documentation/Form.rst
View file @
1260b56e
...
...
@@ -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
...
...
Documentation/Installation.rst
View file @
1260b56e
...
...
@@ -42,7 +42,7 @@ The following features are only tested / supported on linux hosts:
* General: QFQ is coded to run on Linux hosts, preferable on Debian derivates like Ubuntu.
* HTML to PDF conversion - command `wkhtmltopdf`.
* Concatenation of PDF files - command `pdfunite`.
* Convert of imges to PDF files - command `img2pdf`.
* Convert of im
a
ges to PDF files - command `img2pdf`.
* PDF decrypt (used for merge with pdfunite) - command `qpdf`.
* PDF decrypt (used for merge with pdfunite) - command `gs` - in case `qpdf` is not successful.
* Mime type detection for uploads - command `file`.
...
...
@@ -65,7 +65,8 @@ For the :ref:`download` function, the programs `img2pdf`, `pdfunite`, `qpdf`, `g
Preparation for Ubuntu::
sudo apt install php-intl
sudo apt install poppler-utils libxrender1 file pdf2svg qpdf ghostscript # for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
# for file upload, PDF and 'HTML to PDF' (wkhtmltopdf), PDF split
sudo apt install poppler-utils libxrender1 file pdf2svg qpdf ghostscript img2pdf
sudo apt install inkscape imagemagick # to render thumbnails
.. _wkhtml:
...
...
Documentation/Links.rst
deleted
100644 → 0
View file @
0cf82050
.. ==================================================
.. ==================================================
.. ==================================================
.. 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>`__
Documentation/Release.rst
View file @
1260b56e
...
...
@@ -48,6 +48,7 @@ Notes
in filenames and wkhtml commandline options (like header/footer).
* Migrate documentation from T3 to ReadTheDocs.io - looks older but 'search' is much more better. New: chapters separated
in individual files.
* For the image to PDF feature, installation of `img2pdf` is required (please check `preparation`_).
Features
^^^^^^^^
...
...
Documentation/index.rst
View file @
1260b56e
...
...
@@ -87,7 +87,6 @@ This documentation is for the TYPO3 extension **qfq**.
ApplicationTest
GeneralTips
Release
Links
License
Sitemap
SearchDocs
...
...
extension/Classes/Core/Constants.php
View file @
1260b56e
...
...
@@ -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'
;
...
...
@@ -1911,6 +1913,7 @@ const EXCEPTION_MESSAGE_DEBUG = SYSTEM_MESSAGE_DEBUG; // Will only be shown as
const
EXCEPTION_FILE
=
'File'
;
const
EXCEPTION_LINE
=
'Line'
;
const
EXCEPTION_CWD
=
'CWD'
;
const
EXCEPTION_STACKTRACE
=
'Stacktrace'
;
const
EXCEPTION_IP_ADDRESS
=
'IP Address'
;
const
EXCEPTION_QFQ_COOKIE
=
'QFQ Cookie'
;
...
...
extension/Classes/Core/Exception/AbstractException.php
View file @
1260b56e
...
...
@@ -103,11 +103,11 @@ class AbstractException extends \Exception {
if
(
isset
(
$arrMsg
[
ERROR_MESSAGE_HTTP_STATUS
]))
{
$this
->
httpStatusCode
=
$arrMsg
[
ERROR_MESSAGE_HTTP_STATUS
];
}
}
$arrDebugHidden
[
EXCEPTION_FILE
]
=
$this
->
getFile
();
$arrDebugHidden
[
EXCEPTION_LINE
]
=
$this
->
getLine
();
$arrDebugHidden
[
EXCEPTION_CWD
]
=
getcwd
();
$arrTrace
=
$this
->
getExtensionTraceAsArray
();
if
(
$store
!==
null
)
{
...
...
extension/Classes/Core/Form/FormAction.php
View file @
1260b56e
...
...
@@ -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,56 +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 succesfully 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
...
...
extension/Classes/Core/Helper/HelperFile.php
View file @
1260b56e
...
...
@@ -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'
;
}
}
extension/Classes/Core/Helper/HelperFormElement.php
View file @
1260b56e
...
...
@@ -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.
\n
SQL Raw: "
.
$fe
[
FE_SQL_VALIDATE
]])
,
ERROR_REPORT_FAILED_ACTION
);
}
}
\ No newline at end of file
extension/Classes/Core/Report/Download.php
View file @
1260b56e
...
...
@@ -200,7 +200,7 @@ class Download {
}
// img2pdf --pagesize A4 -o out.pdf *.jpg
$cmd
=
$this
->
img2Pdf
.
' --pagesize A4 -o '
.
$filePdf
.
' '
.
escapeshellarg
(
$fileImage
);
$cmd
=
$this
->
img2Pdf
.
' --pagesize A4 -o '
.
$filePdf
.
' '
.
escapeshellarg
(
$fileImage
)
.
' 2>&1'
;
if
(
$this
->
downloadDebugLog
!=
''
)
{
Logger
::
logMessage
(
"Download:
$cmd
"
,
$this
->
downloadDebugLog
);
...
...
@@ -266,7 +266,7 @@ class Download {
if
(
$rcQpdf
!=
0
)
{
// Try 2: via 'gs -sDEVICE=pdfwrite'
$cmdGs
=
$this
->
gs
.
" -sDEVICE=pdfwrite -dNOPAUSE -sOutputFile=
\"
$file
\"
--
\"
$backup
\"
"
;
$cmdGs
=
$this
->
gs
.
" -sDEVICE=pdfwrite -dNOPAUSE -sOutputFile=
\"
$file
\"
--
\"
$backup
\"
2>&1
"
;
exec
(
$cmdGs
,
$outputGs
,
$rcGs
);
if
(
$rcGs
!=
0
)
{
...
...
extension/Classes/Core/Save.php
View file @
1260b56e
...
...
@@ -20,6 +20,7 @@ use IMATHUZH\Qfq\Core\Helper\Support;
use
IMATHUZH\Qfq\Core\Store\FillStoreForm
;
use
IMATHUZH\Qfq\Core\Store\Sip
;
use
IMATHUZH\Qfq\Core\Store\Store
;
use
ZipArchive
;
/**
* Class Save
...
...
@@ -254,7 +255,7 @@ class Save {
$formValues
=
$this
->
createEmptyTemplateGroupElements
(
$formValues
);
// Iterate over all table.columns. Built an assoc array $newValues.
foreach
(
$tableColumns
AS
$column
)
{
foreach
(
$tableColumns
as
$column
)
{
// Never save a predefined 'id': autoincrement values will be given by database..
if
(
$column
===
COLUMN_ID
)
{
...
...
@@ -408,7 +409,7 @@ class Save {
*/
private
function
isColumnUploadField
(
$feName
)
{
foreach
(
$this
->
feSpecNative
AS
$formElement
)
{
foreach
(
$this
->
feSpecNative
as
$formElement
)
{
if
(
$formElement
[
FE_NAME
]
===
$feName
&&
$formElement
[
FE_TYPE
]
==
FE_TYPE_UPLOAD
)
return
true
;
}
...
...
@@ -501,12 +502,22 @@ class Save {
$sip
=
new
Sip
(
false
);
$newValues
=
array
();
$vars
=
array
();
$flagDoUnzip
=
false
;
$formValues
=
$this
->
store
->
getStore
(
STORE_FORM
);
$primaryRecord
=
$this
->
store
->
getStore
(
STORE_RECORD
);
// necessary to check if the current formElement exist as a column of the primary table.
foreach
(
$this
->
feSpecNative
AS
$formElement
)
{
// Upload - Take care the necessary target directories exist.
$cwd
=
getcwd
();
$sitePath
=
$this
->
store
->
getVar
(
SYSTEM_SITE_PATH
,
STORE_SYSTEM
);
if
(
$cwd
===
false
||
$sitePath
===
false
||
!
HelperFile
::
chdir
(
$sitePath
))
{
throw
new
\
UserFormException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
'getcwd() failed or SITE_PATH undefined or chdir() failed'
,
ERROR_MESSAGE_TO_DEVELOPER
=>
"getcwd() failed or SITE_PATH undefined or chdir('
$sitePath
') failed."
]),
ERROR_IO_CHDIR
);
}
foreach
(
$this
->
feSpecNative
as
$formElement
)
{
// skip non upload formElements
if
(
$formElement
[
FE_TYPE
]
!=
FE_TYPE_UPLOAD
)
{
continue
;
...
...
@@ -523,7 +534,34 @@ class Save {
}
$column
=
$formElement
[
FE_NAME
];
$statusUpload
=
$this
->
store
->
getVar
(
$formValues
[
$column
]
??
''
,
STORE_EXTRA
);
// Get file stats
$vars
=
array
();
$vars
[
VAR_FILE_SIZE
]
=
$statusUpload
[
FILES_SIZE
]
??
''
;
$vars
[
VAR_FILE_MIME_TYPE
]
=
$statusUpload
[
FILES_TYPE
]
??
''
;
// Check for 'unzip'.
if
(
isset
(
$formElement
[
FE_FILE_UNZIP
])
&&
$formElement
[
FE_FILE_UNZIP
]
!=
'0'
&&
$vars
[
VAR_FILE_MIME_TYPE
]
==
'application/zip'
)
{
$flagDoUnzip
=
true
;
}
// Do upload
$pathFileName
=
$this
->
doUpload
(
$formElement
,
(
$formValues
[
$column
]
??
''
),
$sip
,
$modeUpload
);
if
(
$flagDoUnzip
&&
$pathFileName
!=
''
)
{
if
(
$formElement
[
FE_FILE_UNZIP
]
==
''
||
$formElement
[
FE_FILE_UNZIP
]
==
'1'
)
{
// Set default dir.
$formElement
[
FE_FILE_UNZIP
]
=
HelperFile
::
joinPathFilename
(
dirname
(
$pathFileName
),
FE_FILE_UNPACK_DIR
);
}
// Backup STORE_VAR - will be changed in doUnzip()
$tmpStoreVar
=
$this
->
store
->
getStore
(
STORE_VAR
);
$this
->
doUnzip
(
$formElement
,
$pathFileName
);
// Restore STORE_VAR
$this
->
store
->
setStore
(
$tmpStoreVar
,
STORE_VAR
,
true
);
}
if
(
$modeUpload
==
UPLOAD_MODE_DELETEOLD
&&
$pathFileName
==
''
)
{
$pathFileNameTmp
=
''
;
// see '4'
...
...
@@ -540,15 +578,15 @@ class Save {
// No new upload and no existing: take care to remove previous upload file statistics.
$this
->
store
->
unsetVar
(
VAR_FILE_MIME_TYPE
,
STORE_VAR
);
$this
->
store
->
unsetVar
(
VAR_FILE_SIZE
,
STORE_VAR
);
$vars
[
VAR_FILE_SIZE
]
=
0
;
$vars
[
VAR_FILE_MIME_TYPE
]
=
''
;
}
else
{
$vars
=
HelperFile
::
getFileStat
(
$pathFileNameTmp
);
$this
->
store
->
appendToStore
(
$vars
,
STORE_VAR
);
}
// If given: fire a sqlBefore query
$this
->
evaluate
->
parse
(
$formElement
[
FE_SQL_BEFORE
]);
if
(
!
$flagDoUnzip
)
{
$this
->
evaluate
->
parse
(
$formElement
[
FE_SQL_BEFORE
]);
}
// Upload Type: Simple or Advanced
// If (isset($primaryRecord[$column])) { - see #5048 - isset does not deal correctly with NULL!
...
...
@@ -567,22 +605,101 @@ class Save {
}
}
elseif
(
isset
(
$formElement
[
FE_IMPORT_TO_TABLE
])
&&
!
isset
(
$formElement
[
FE_SLAVE_ID
]))
{
// Excel import on nonexisting column -> no upload
}
elseif
(
$flagDoUnzip
)
{
// If ZIP and advanced upload: process it not here but via doUnzip.
}
else
{
// 'Advanced Upload'
$this
->
doUploadSlave
(
$formElement
,
$modeUpload
);
}
// If given: fire a sqlAfter query
$this
->
evaluate
->
parse
(
$formElement
[
FE_SQL_AFTER
]);
if
(
!
$flagDoUnzip
)
{
$this
->
evaluate
->
parse
(
$formElement
[
FE_SQL_AFTER
]);
}
}
// Clean up
HelperFile
::
chdir
(
$cwd
);
// Only used in 'Simple Upload'
if
(
count
(
$newValues
)
>
0
)
{
$this
->
updateRecord
(
$this
->
formSpec
[
F_TABLE_NAME
],
$newValues
,
$recordId
,
$this
->
formSpec
[
F_PRIMARY_KEY
]);
}
}
/**
* Unzip $pathFileName to $formElement[FE_FILE_UNZIP]. Before final extract, fire FE_SQL_VALIDATE.
* For each file in ZIP:
* - Fill STORE_VAR with VAR_FILENAME, VAR_FILENAME_ONLY, VAR_FILENAME_BASE, VAR_FILENAME_EXT, VAR_FILE_MIME_TYPE, VAR_FILE_SIZE.
* - Fire $formElement[FE_SQL_VALIDATE]
* - Fire FE_SLAVE_ID, FE_SQL_BEFORE, FE_SQL_INSERT, FE_SQL_UPDATE, FE_SQL_DELETE, FE_SQL_AFTER
*
* @param array $formElement
* @param string $pathFileName
* @throws \CodeException
* @throws \DbException
* @throws \UserFormException
* @throws \UserReportException
*/
private
function
doUnzip
(
array
$formElement
,
$pathFileName
)
{
if
(
!
is_readable
(
$pathFileName
))
{
throw
new
\
UserFormException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
"Open ZIP file failed"
,
ERROR_MESSAGE_TO_DEVELOPER
=>
"File: "
.
$pathFileName
]),
ERROR_IO_ZIP_OPEN
);
}
$zip
=
new
ZipArchive
();
$res
=
$zip
->
open
(
$pathFileName
);
if
(
$res
!==
true
)
{
throw
new
\
UserFormException
(
json_encode
([
ERROR_MESSAGE_TO_USER
=>
"Open ZIP file failed"
.
HelperFile
::
zipFileErrMsg
(
$res
),
ERROR_MESSAGE_TO_DEVELOPER
=>
"File: "
.
$pathFileName
]),
ERROR_IO_ZIP_OPEN
);
}
// Extract
if
(
false
===
$zip
->
extractTo
(
$formElement
[
FE_FILE_UNZIP
]))
{
throw
new
\
UserFormException
(
"Failed to extract ZIP."
,
ERROR_IO_ZIP_OPEN
);
}
// Do sqlValidate() - to get mime type of zipped items, the archive has already been extracted.
if
(
!
empty
(
$formElement
[
FE_SQL_VALIDATE
]))
{
for
(
$i
=
0
;
$i
<
$zip
->
numFiles
;
$i
++
)
{
$stat
=
$zip
->
statIndex
(
$i
);
$itemPathFileName
=
HelperFile
::
joinPathFilename
(
$formElement
[
FE_FILE_UNZIP
],
$stat
[
'name'
]);
$this
->
store
->
appendToStore
(
HelperFile
::
getFileStat
(
$itemPathFileName
),
STORE_VAR
);
$this
->
store
->
appendToStore
(
HelperFile
::
pathinfo
(
$itemPathFileName
),
STORE_VAR
);
HelperFormElement
::
sqlValidate
(
$this
->
evaluate
,
$formElement
);
}
}
// Process
if
(
!
isset
(
$formElement
[
FE_SLAVE_ID
]))
{
$formElement
[
FE_SLAVE_ID
]
=
''
;
}
if
(
!
empty
(
$formElement
[
FE_SLAVE_ID
]
.
$formElement
[
FE_SQL_BEFORE
]
.
$formElement
[
FE_SQL_INSERT
]
.
$formElement
[
FE_SQL_UPDATE
]
.
$formElement
[
FE_SQL_DELETE
]
.
$formElement
[
FE_SQL_AFTER
]))
{
for
(
$i
=
0
;
$i
<
$zip
->
numFiles
;
$i
++
)
{
$stat
=
$zip
->
statIndex
(
$i
);
$itemPathFileName
=
HelperFile
::
joinPathFilename
(
$formElement
[
FE_FILE_UNZIP
],
$stat
[
'name'
]);
$this
->
store
->
appendToStore
(
HelperFile
::
getFileStat
(
$itemPathFileName
),
STORE_VAR
);
$this
->
store
->
appendToStore
(
HelperFile
::
pathinfo
(