Commit aeb35cd7 authored by Elias Villiger's avatar Elias Villiger
Browse files

Merge branch 'master' into F6596-download-tt_content-uid

parents 9de840af 8203e8f7
Pipeline #958 passed with stage
in 1 minute and 39 seconds
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.10.0
---------------
Date: 04.10.2018
Notes
^^^^^
Features
^^^^^^^^
* #6894 / Upload: chmod for file creation.
* #6886 / Upload: Auto Orient - implementation.
* #6721: Log switching {{feUser:U}} to qfq.log. Log {{feUser:U}} to sql.log.
* #5458: Add '{{feUser:U}}' to be shown on exception.
* Manual.rst: Fix missing single tick in special column name '_=<var>'
* Report: Variables copied to STORE_USER via "... AS '_=<varname>'" are now available in STORE_RECORD by {{<varname>:R}}
Bug Fixes
^^^^^^^^^
* #6902 / Drag and drop in subrecords: expect 1 row got nothing.
Version 18.9.2
--------------
......
......@@ -44,12 +44,12 @@ Neue Versionsnummer
* Update the version number in this document (topic 6)
* Commit & Push new version changes to master branch:
New version 18.9.2
New version 18.10.0
6) **New Tag**:
git tag v18.9.2
git push -u origin v18.9.2
git tag v18.10.0
git push -u origin v18.10.0
7) PhpStorm: **Sync** all files to VM qfq.
......
......@@ -191,22 +191,22 @@ Setup CSS & JS
^^^^^^^^^^^^^^
::
page.meta {
X-UA-Compatible = IE=edge
X-UA-Compatible.attribute = http-equiv
viewport=width=device-width, initial-scale=1
}
page.meta {
X-UA-Compatible = IE=edge
X-UA-Compatible.attribute = http-equiv
viewport=width=device-width, initial-scale=1
}
page.includeCSS {
file01 = typo3conf/ext/qfq/Resources/Public/Css/bootstrap.min.css
file02 = typo3conf/ext/qfq/Resources/Public/Css/bootstrap-theme.min.css
file03 = typo3conf/ext/qfq/Resources/Public/Css/jqx.base.css
file04 = typo3conf/ext/qfq/Resources/Public/Css/jqx.bootstrap.css
file05 = typo3conf/ext/qfq/Resources/Public/Css/qfq-bs.css
file06 = typo3conf/ext/qfq/Resources/Public/Css/tablesorter-bootstrap.css
}
page.includeCSS {
file01 = typo3conf/ext/qfq/Resources/Public/Css/bootstrap.min.css
file02 = typo3conf/ext/qfq/Resources/Public/Css/bootstrap-theme.min.css
file03 = typo3conf/ext/qfq/Resources/Public/Css/jqx.base.css
file04 = typo3conf/ext/qfq/Resources/Public/Css/jqx.bootstrap.css
file05 = typo3conf/ext/qfq/Resources/Public/Css/qfq-bs.css
file06 = typo3conf/ext/qfq/Resources/Public/Css/tablesorter-bootstrap.css
}
page.includeJS {
page.includeJS {
file01 = typo3conf/ext/qfq/Resources/Public/JavaScript/jquery.min.js
file02 = typo3conf/ext/qfq/Resources/Public/JavaScript/bootstrap.min.js
file03 = typo3conf/ext/qfq/Resources/Public/JavaScript/validator.min.js
......@@ -223,8 +223,7 @@ Setup CSS & JS
# Only needed in case FormElement 'annotate' is used.
file20 = typo3conf/ext/qfq/Resources/Public/JavaScript/fabric.min.js
file21 = typo3conf/ext/qfq/Resources/Public/JavaScript/qfq.fabric.min.js
}
}
.. _form-editor:
......@@ -1542,7 +1541,7 @@ Store: *SIP* - S
+-------------------------+-----------------------------------------------------------+
| table | current table name |
+-------------------------+-----------------------------------------------------------+
| urlparam | all non Typo3 parameter in one string |
| urlparam | all non Typo3 parameter in one string |
+-------------------------+-----------------------------------------------------------+
| <user defined> | additional user defined link parameter |
+-------------------------+-----------------------------------------------------------+
......@@ -1768,17 +1767,17 @@ To decide which Parameter should be placed on *Form.parameter* and which on *For
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| ldapAttributes | cn, email | List of attributes to save in STORE_LDAP | x | x | FSL |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| ldapSearch | (mail=john.doe@example.com) | Regular LDAP search expression | x | x | FSL |
| ldapSearch | (mail=john.doe@example.com) | Regular LDAP search expression | x | x | FSL |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| ldapTimeLimit | 3 (default) | Maximum time to wait for an answer of the LDAP Server | x | x | TA, FSL |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| ldapUseBindCredentials | ldapUseBindCredentials=1 | Use LDAP_1_* credentials from config-qfq-php_ for ldap_bind()| x | x | TA, FSL |
| ldapUseBindCredentials | ldapUseBindCredentials=1 | Use LDAP_1_* credentials from config-qfq-php_ for ldap_bind() | x | x | TA, FSL |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdap | - | Enable LDAP as 'Typeahead' data source | | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapSearch | `(|(cn=*?*)(mail=*?*))` | Regular LDAP search expression, returns upto typeAheadLimit | x | x | TA |
| typeAheadLdapSearch | `(|(cn=*?*)(mail=*?*))` | Regular LDAP search expression, returns upto typeAheadLimit | x | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapSearchPrefetch | `(mail=?)` | Regular LDAP search expression, typically return one record | x | x | TA |
| typeAheadLdapSearchPrefetch | `(mail=?)` | Regular LDAP search expression, typically return one record | x | x | TA |
+-----------------------------+----------------------------------+---------------------------------------------------------------+------+-------------+----------+
| typeAheadLdapSearchPerToken | - | Split search value in token and OR-combine every search with | x | x | TA |
| | | the individual tokens | | | |
......@@ -2658,7 +2657,7 @@ Fields:
+---------------------+-----------------------------+-----------------------------------------------------------------------------------------------------+
|tabindex | string |HTML tabindex attribute _`field-tabindex` |
+---------------------+-----------------------------+-----------------------------------------------------------------------------------------------------+
|Size | string |Visible length of input element. Might be omitted, depending on the chosen form layout. |
|Size | string |Visible length of input element. Might be omitted, depending on the chosen form layout. |
| | |Format: <width>,<height> (in characters) _`field-size` |
+---------------------+-----------------------------+-----------------------------------------------------------------------------------------------------+
|BS Label Columns | string | Number of bootstrap grid columns for label. By default empty, value inherits from the form. |
......@@ -2819,8 +2818,12 @@ See also at specific *FormElement* definitions.
| accept | string | |
| maxFileSize | string | |
| fileDestination | string | |
| slaveId | string | |
| fileReplace | string | |
| autoOrient | string | |
| autoOrientCmd | string | |
| autoOrientMimeType | string | |
| chmodFile / chmodDir | string | |
| slaveId | string | |
| sqlBefore | string | |
| sqlInsert | string | |
| sqlUpdate | string | |
......@@ -3414,8 +3417,8 @@ Inside the *Form editor* it's shown as a 'native FormElement'.
During saving the current record, it behaves like an action FormElement
and will be processed after saving the primary record and before any action FormElements are processed.
* *FormElement.value* = `<string>` - By default, the full path of any already uploaded file is shown. To show something different, e.g.
only the filename, define: ::
* *FormElement.value* = `<string>` - By default, the full path of any already uploaded file is shown. To show something
different, e.g. only the filename, define: ::
a) {{filenameBase:V}}
b) {{SELECT SUBSTRING_INDEX( '{{pathFileName:R}}', '/', -1) }}
......@@ -3477,7 +3480,7 @@ See also `downloadButton`_ to offer a download of an uploaded file.
record, if table columns `fileSize` and/or `mimeType` exist.
* If there are more than one Upload FormElement in a form, the automatically update for `fileSize` and/or `mimeType`
are not useful - the columns only handle
are not done automatically.
* In :ref:`Upload advanced mode` the `fileSize` and / or `mimeType` have to be updated with an explicit SQL statement::
......@@ -3485,6 +3488,22 @@ See also `downloadButton`_ to offer a download of an uploaded file.
* *fileReplace* = `always` - If `fileDestination` exist - replace it by the new one.
* *chmodFile* = <unix file permission mode> - e.g. `660` for owner and group read and writeable. Only the numeric mode is allowed.
* *chmodDir* = <unix file permission mode> - e.g. `770` for owner and group read, writeable and executable. Only the
numeric mode is allowed. Will be applied to all new created directories.
* autoOrient: images might contain EXIF data (e.g. captured via mobile phones) incl. an orientation tag like TopLeft,
BottomRight and so on. Web-Browser and other grafic programs often understand and respect those information and rotate
such images automatically. If not, the image might be displayed in an unwanted oritentation.
With active option 'autoOrient', QFQ tries to normalize such images via 'convert' (part of ImageMagick). Especially
if images are processed by the QFQ internal 'Fabric'-JS it's recommended to normalize images first. The normalization
process does not solve all orientation problems.
* *autoOrient* = [0|1]
* *autoOrientCmd* = 'convert -auto-orient {{fileDestination:V}} {{fileDestination:V}}.new; mv {{fileDestination:V}}.new {{fileDestination:V}}'
* *autoOrientMimeType* = image/jpeg,image/png,image/tiff
If the defaults for `autoOrientCmd` and `autoOrientMimeType` are sufficient, it's not necessary to specify them.
.. _`downloadButton`:
......
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.10.0
---------------
Date: 04.10.2018
Notes
^^^^^
Features
^^^^^^^^
* #6894 / Upload: chmod for file creation.
* #6886 / Upload: Auto Orient - implementation.
* #6721: Log switching {{feUser:U}} to qfq.log. Log {{feUser:U}} to sql.log.
* #5458: Add '{{feUser:U}}' to be shown on exception.
* Manual.rst: Fix missing single tick in special column name '_=<var>'
* Report: Variables copied to STORE_USER via "... AS '_=<varname>'" are now available in STORE_RECORD by {{<varname>:R}}
Bug Fixes
^^^^^^^^^
* #6902 / Drag and drop in subrecords: expect 1 row got nothing.
Version 18.9.2
--------------
......
......@@ -2,8 +2,8 @@
[general]
project = QFQ - Quick Form Query
version = 18.9
release = 18.9.2
version = 18.10
release = 18.10.0
t3author = Carsten Rose
copyright = since 2017 by the author
......
......@@ -57,9 +57,9 @@ copyright = u'2017, Carsten Rose'
# built documents.lease
#
# The short X.Y version.
version = '18.9'
version = '18.10'
# The full version, including alpha/beta/rc tags.
release = '18.9.2'
release = '18.10.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -37,6 +37,29 @@ Bug Fixes
^^^^^^^^^
Version 18.10.0
---------------
Date: 04.10.2018
Notes
^^^^^
Features
^^^^^^^^
* #6894 / Upload: chmod for file creation.
* #6886 / Upload: Auto Orient - implementation.
* #6721: Log switching {{feUser:U}} to qfq.log. Log {{feUser:U}} to sql.log.
* #5458: Add '{{feUser:U}}' to be shown on exception.
* Manual.rst: Fix missing single tick in special column name '_=<var>'
* Report: Variables copied to STORE_USER via "... AS '_=<varname>'" are now available in STORE_RECORD by {{<varname>:R}}
Bug Fixes
^^^^^^^^^
* #6902 / Drag and drop in subrecords: expect 1 row got nothing.
Version 18.9.2
--------------
......
......@@ -26,7 +26,6 @@ sendEMailOptions =
dateFormat = dd.mm.yyyy
# cat=dynamic/config; type=string; label=Fill store 'SYSTEM' by SQL 1:Default is empty. SQL query fired during QFQ load. The result have to be exactly one row. That row will be merged to store 'SYSTEM'. Retrieve values via '{{column:Y}}'. Example 'SELECT id AS _periodId FROM Period WHERE start<=NOW() ORDER BY start DESC LIMIT 1'
fillStoreSystemBySql1 =
......
......@@ -11,7 +11,7 @@ $EM_CONF[$_EXTKEY] = array(
'dependencies' => 'fluid,extbase',
'clearcacheonload' => true,
'state' => 'stable',
'version' => '18.9.2',
'version' => '18.10.0',
'constraints' => [
'depends' => [
'typo3' => '6.0.0-9.2.99',
......
......@@ -40,19 +40,21 @@ $answer[API_STATUS] = API_ANSWER_STATUS_ERROR;
$answer[API_MESSAGE] = '';
try {
try {
$fileUpload = new File();
$fileUpload = new File();
$fileUpload->process();
$fileUpload->process();
$answer[API_MESSAGE] = 'upload: success';
$answer[API_MESSAGE] = 'upload: success';
// $answer[API_REDIRECT] = API_ANSWER_REDIRECT_NO;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
$answer[API_STATUS] = API_ANSWER_STATUS_SUCCESS;
} catch (qfq\UserFormException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
} catch (qfq\CodeException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
} catch (qfq\UserFormException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
} catch (qfq\CodeException $e) {
$answer[API_MESSAGE] = $e->formatMessage();
}
} catch (\Exception $e) {
$answer[API_MESSAGE] = "Generic Exception: " . $e->getMessage();
}
......
......@@ -260,7 +260,11 @@ abstract class AbstractBuildForm {
} else {
$recordId = $this->store->getVar(SIP_RECORD_ID, STORE_SIP);
if (!($recordId == '' || is_numeric($recordId))) {
throw new UserFormException('Invalid record ID: r="' . $recordId, '"', ERROR_INVALID_VALUE);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Invalid record ID', ERROR_MESSAGE_SUPPORT => 'Invalid record ID: r="' . $recordId]),
ERROR_INVALID_VALUE);
}
$htmlElements = $this->elements($recordId, $filter, 0, $json, $modeCollectFe, $htmlElementNameIdZero, $storeUse, $mode);
......@@ -1392,7 +1396,10 @@ abstract class AbstractBuildForm {
}
if (false === stristr(substr($sqlTest, 0, 7), 'SELECT ')) {
throw new UserFormException("Expect a SELECT statement in " . FE_TYPEAHEAD_SQL . " - got: " . $sqlTest, ERROR_BROKEN_PARAMETER);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => '"Expect a SELECT statement', ERROR_MESSAGE_SUPPORT => "Expect a SELECT statement in " . FE_TYPEAHEAD_SQL . " - got: " . $sqlTest]),
ERROR_BROKEN_PARAMETER);
}
if (false === stristr($sql, ' LIMIT ')) {
......@@ -2951,6 +2958,17 @@ abstract class AbstractBuildForm {
$arr[FE_FILE_MAX_FILE_SIZE] = empty($formElement[FE_FILE_MAX_FILE_SIZE]) ? UPLOAD_DEFAULT_MAX_SIZE : $formElement[FE_FILE_MAX_FILE_SIZE];
$arr[FE_FILE_MAX_FILE_SIZE] = Support::returnBytes($arr[FE_FILE_MAX_FILE_SIZE]);
// Check Safari Bug #5578: in case Safari (Mac OS X or iOS) loads an 'upload element' with more than one filetype, fall back to 'no preselection'.
if (strpos($formElement[FE_FILE_MIME_TYPE_ACCEPT], ',') !== false) {
$ua = $this->store->getVar('HTTP_USER_AGENT', STORE_CLIENT, SANITIZE_ALLOW_ALNUMX);
// Look for " Version/11.0 Mobile/15A5370a Safari/" or " Version/9.0.2 Safari/"
$rc = preg_match(' Version\/.*Safari\/', $ua, $matches);
// But not like " Version/4.0 Chrome/52.0.2743.98 Safari/"
if ($rc == 1 && false === strpos($matches[0], ' Chrome/')) {
$formElement[FE_FILE_MIME_TYPE_ACCEPT] = '';
}
}
if ((Support::returnBytes(ini_get('post_max_size')) < $arr[FE_FILE_MAX_FILE_SIZE]) ||
(Support::returnBytes(ini_get('upload_max_filesize')) < $arr[FE_FILE_MAX_FILE_SIZE])
) {
......
......@@ -42,9 +42,11 @@ class BodytextParser {
$bodytext = Support::decryptDoubleCurlyBraces($bodytext);
if (strpos($bodytext, NESTING_TOKEN_OPEN) !== false) {
throw new UserFormException("Missing close delimiter: $bodytext", ERROR_MISSING_CLOSE_DELIMITER);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Report: Missing close delimiter', ERROR_MESSAGE_SUPPORT => $bodytext]), ERROR_MISSING_CLOSE_DELIMITER);
}
return $bodytext;
}
......@@ -279,7 +281,10 @@ class BodytextParser {
if ($posMatchOpen === false) {
$result = $this->decryptNestingDelimeter($result, $nestingOpen, $nestingClose);
throw new UserFormException("Missing open delimiter: $result", ERROR_MISSING_OPEN_DELIMITER);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Missing open delimiter', ERROR_MESSAGE_SUPPORT => "Missing open delimiter: $result"]),
ERROR_MISSING_OPEN_DELIMITER);
}
$pre = substr($result, 0, $posMatchOpen);
......
......@@ -213,6 +213,7 @@ const ERROR_STORE_KEY_EXIST = 1201;
// I/O Error
const ERROR_IO_READ_FILE = 1300;
const ERROR_IO_COPY = 1301;
const ERROR_IO_WRITE = 1303;
const ERROR_IO_OPEN = 1304;
const ERROR_IO_UNLINK = 1305;
......@@ -224,6 +225,7 @@ const ERROR_IO_CHDIR = 1310;
const ERROR_IO_CREATE_FILE = 1311;
const ERROR_IO_COPY_FILE = 1312;
const ERROR_IO_FILE_NOT_FOUND = 1313;
const ERROR_IO_CHMOD = 1314;
//Report
const ERROR_UNKNOWN_LINK_QUALIFIER = 1400;
......@@ -979,6 +981,12 @@ const FE_FILE_SPLIT = 'fileSplit';
const FE_FILE_SPLIT_SVG = 'svg';
const FE_FILE_SPLIT_TABLE_NAME = 'tableNameSplit';
const FE_FILE_DOWNLOAD_BUTTON = 'downloadButton';
const FE_FILE_AUTO_ORIENT = 'autoOrient';
const FE_FILE_AUTO_ORIENT_CMD = 'autoOrientCmd';
const FE_FILE_AUTO_ORIENT_CMD_DEFAULT = 'convert -auto-orient {{fileDestination:V}} {{fileDestination:V}}.new; mv {{fileDestination:V}}.new {{fileDestination:V}}';
const FE_FILE_AUTO_ORIENT_MIME_TYPE = 'autoOrientMimeType';
const FE_FILE_CHMOD_FILE = 'chmodFile';
const FE_FILE_CHMOD_DIR = 'chmodDir';
// Excel Import
const FE_IMPORT_TO_TABLE = 'importToTable';
......
......@@ -71,7 +71,10 @@ class Delete {
$cwd = getcwd();
$sitePath = $this->store->getVar(SYSTEM_SITE_PATH, STORE_SYSTEM);
if ($cwd === false || $sitePath === false || !chdir($sitePath)) {
throw new UserFormException("getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed.", ERROR_IO_CHDIR);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'getcwd() failed or SITE_PATH undefined or chdir() failed', ERROR_MESSAGE_SUPPORT => "getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed."]),
ERROR_IO_CHDIR);
}
// Read record first.
......@@ -82,7 +85,10 @@ class Delete {
$this->db->sql("DELETE FROM $tableName WHERE $primaryKey =? LIMIT 1", ROW_REGULAR, [$recordId]);
} else {
throw new UserFormException("Record $recordId not found in table '$tableName'.", ERROR_RECORD_NOT_FOUND);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Record not found in table', ERROR_MESSAGE_SUPPORT => "Record $recordId not found in table '$tableName'."]),
ERROR_RECORD_NOT_FOUND);
}
chdir($cwd);
......@@ -119,7 +125,9 @@ class Delete {
$samePathFileName = $this->db->sql("SELECT COUNT($primaryKey) AS cnt FROM $tableName WHERE $key LIKE ?", ROW_EXPECT_1, [$file]);
if ($samePathFileName['cnt'] === 1) {
if (!unlink($file)) {
throw new UserFormException("Error deleting file: $file", ERROR_IO_UNLINK);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Error deleting file', ERROR_MESSAGE_SUPPORT => "Error deleting file: $file"]),
ERROR_IO_UNLINK);
}
}
}
......
......@@ -127,7 +127,9 @@ class Evaluate {
$flagTokenReplaced = false;
if ($recursion > 4) {
throw new qfq\UserFormException("Recursion too deep ($recursion). Line: $line", ERROR_RECURSION_TOO_DEEP);
throw new qfq\UserFormException(json_encode([ERROR_MESSAGE_TO_USER => 'Recursion too deep', ERROR_MESSAGE_SUPPORT => "Recursion too deep ($recursion). Line: $line"]),
ERROR_RECURSION_TOO_DEEP);
}
$result = $line;
......@@ -143,7 +145,9 @@ class Evaluate {
$posMatchOpen = strrpos(substr($result, 0, $posFirstClose), $this->startDelimiter);
if ($posMatchOpen === false) {
throw new UserFormException("Missing open delimiter: $result", ERROR_MISSING_OPEN_DELIMITER);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Missing open delimiter', ERROR_MESSAGE_SUPPORT => "Missing open delimiter: $result"]),
ERROR_MISSING_OPEN_DELIMITER);
}
$pre = substr($result, 0, $posMatchOpen);
......
......@@ -123,13 +123,15 @@ class File {
$statusUpload = array_merge($statusUpload, $newArr);
if ($statusUpload[FILES_ERROR] !== UPLOAD_ERR_OK) {
throw new UserFormException($this->uploadErrMsg[$newArr[FILES_ERROR]], ERROR_UPLOAD);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Upload: Error', ERROR_MESSAGE_SUPPORT => $this->uploadErrMsg[$newArr[FILES_ERROR]]]),
ERROR_UPLOAD);
}
$this->checkMaxFileSize($statusUpload['size']);
$accept = $this->store->getVar(FE_FILE_MIME_TYPE_ACCEPT, STORE_SIP);
if (!$this->checkFileType($statusUpload['tmp_name'], $statusUpload['name'], $accept)) {
if (!HelperFile::checkFileType($statusUpload['tmp_name'], $statusUpload['name'], $accept)) {
throw new UserFormException('Filetype not allowed. Allowed: ' . $accept, ERROR_UPLOAD_FILE_TYPE);
}
......@@ -140,63 +142,6 @@ class File {
$this->store->setVar($sipUpload, $statusUpload, STORE_EXTRA);
}
/**
* Checks the file filetype against the allowed mimetype definition. Return true as soon as one match is found.
* Types recognized:
* * 'mime type' as delivered by `file` which matches a definition on
* http://www.iana.org/assignments/media-types/media-types.xhtml
* * Joker based: audio/*, video/*, image/*
* * Filename extension based: .pdf,.doc,..
*
* @param string $tmp_name
* @param string $name
* @param string $accept
*
* @return bool
* @throws UserFormException
*/
private function checkFileType($tmp_name, $name, $accept) {
// E.g.: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary'
$fileMimeType = HelperFile::getMimeType($tmp_name);
// Strip optional '; charset=binary'
$arr = explode(';', $fileMimeType, 2);
$fileMimeType = $arr[0];
// Split between 'Media Type' and 'Media Subtype'
$fileMimeTypeSplitted = explode('/', $arr[0], 2);
$path_parts = pathinfo($name); // to extract the filename extension of the uploaded file.
// Process all listed mimetypes (incl. filename extension and joker)
// $accept e.g.: 'image/*,application/pdf,.pdf'
$arr = explode(',', $accept); // Split multiple defined mimetypes/extensions in single chunks.
foreach ($arr as $listElementMimeType) {
$listElementMimeType = trim(strtolower($listElementMimeType));
if ($listElementMimeType == '') {
continue; // will be skipped
} elseif ($listElementMimeType[0] == '.') { // Check for definition 'filename extension'
if ('.' . strtolower($path_parts['extension']) == $listElementMimeType) {
return true;
}
} else {
// Check for Joker, e.g.: 'image/*'
$splitted = explode('/', $listElementMimeType, 2);
if ($splitted[1] == '*') {
if ($splitted[0] == $fileMimeTypeSplitted[0]) {
return true;
}
} elseif ($fileMimeType == $listElementMimeType) {
return true;
}
}
}
return false;
}
/**
* @param $sipUpload
* @param $statusUpload
......@@ -211,7 +156,9 @@ class File {
$file = Support::extendFilename($statusUpload[FILES_TMP_NAME], UPLOAD_CACHED);
if (file_exists($file)) {
if (!unlink($file)) {
throw new UserFormException('unlink file: ' . $file, ERROR_IO_UNLINK);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Unlink file', ERROR_MESSAGE_SUPPORT => 'unlink file: ' . $file]),
ERROR_IO_UNLINK);
}
}
$statusUpload[FILES_TMP_NAME] = '';
......
......@@ -443,9 +443,9 @@ class Save {
$this->feSpecNative[$key][FE_MODE_SQL] = $mode;
}
if(isset($formElement[FE_ACCEPT_ZERO_AS_REQUIRED]) && $formElement[FE_ACCEPT_ZERO_AS_REQUIRED] != '0' &&
isset($clientValues[$formElement[FE_NAME]]) && $clientValues[$formElement[FE_NAME]]=='0'){
$mode='fake'; // The next if() should never be true.
if (isset($formElement[FE_ACCEPT_ZERO_AS_REQUIRED]) && $formElement[FE_ACCEPT_ZERO_AS_REQUIRED] != '0' &&
isset($clientValues[$formElement[FE_NAME]]) && $clientValues[$formElement[FE_NAME]] == '0') {
$mode = 'fake'; // The next if() should never be true.
}
if (!$requiredOff && $mode == FE_MODE_REQUIRED && empty($clientValues[$formElement[FE_NAME]])) {
......@@ -471,14 +471,18 @@ class Save {
$cwd = getcwd();
$sitePath = $this->store->getVar(SYSTEM_SITE_PATH, STORE_SYSTEM);
if ($cwd === false || $sitePath === false || !chdir($sitePath)) {
throw new UserFormException("getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed.", ERROR_IO_CHDIR);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'getcwd() failed or SITE_PATH undefined or chdir() failed', ERROR_MESSAGE_SUPPORT => "getcwd() failed or SITE_PATH undefined or chdir('$sitePath') failed."]),
ERROR_IO_CHDIR);
}
// Get original pathFileName
$field = HelperFormElement::AppendFormElementNameImageCut($formElement);
$pathFileName = $this->store->getVar($field, STORE_SIP);
if ($pathFileName == '' || !file_exists($pathFileName)) {
throw new UserFormException('Empty file or file not found: ' . $pathFileName, ERROR_IO_FILE_NOT_FOUND);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Empty file or file not found', ERROR_MESSAGE_SUPPORT => 'Empty file or file not found: ' . $pathFileName]),
ERROR_IO_FILE_NOT_FOUND);
}
// 'data:image/png;base64,AAAFBfj42Pj4...';
......@@ -515,7 +519,9 @@ class Save {
!file_exists($pathParts['dirname'] . $pathParts['filename'] . $extSave)
) {
if (!rename($pathFileName, $pathFileName . $extSave)) {
throw new UserFormException("Rename file: '$pathFileName' > '$pathFileName$extSave'", ERROR_IO_RENAME);
throw new UserFormException(
json_encode([ERROR_MESSAGE_TO_USER => 'Rename file', ERROR_MESSAGE_SUPPORT => "Rename file: '$pathFileName' > '$pathFileName$extSave'"]),
ERROR_IO_RENAME);
}
}