diff --git a/extension/Documentation/Manual.rst b/extension/Documentation/Manual.rst
index 31ac84e52a98ae08458e5ca2b54a5246a50cbd03..d7bed9b167e9cb324598032e6ceefa775d5154ef 100644
--- a/extension/Documentation/Manual.rst
+++ b/extension/Documentation/Manual.rst
@@ -2790,31 +2790,39 @@ See also at specific *FormElement* definitions.
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| autofocus | string | See `input-option-autofocus`_ |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| capture | string | See `input-upload`_ |
-| accept | string | |
-| maxFileSize | string | |
-| fileDestination | string | |
-| fileReplace | string | |
-| autoOrient | string | |
-| autoOrientCmd | string | |
-| autoOrientMimeType | string | |
-| chmodFile / chmodDir | string | |
-| slaveId | string | |
-| sqlBefore | string | |
-| sqlInsert | string | |
-| sqlUpdate | string | |
-| sqlDelete | string | |
-| sqlAfter | string | |
+| capture, | string | See `input-upload`_ |
+| accept, | | |
+| maxFileSize, | | |
+| fileDestination, | | |
+| fileReplace, | | |
+| autoOrient, | | |
+| autoOrientCmd, | | |
+| autoOrientMimeType, | | |
+| chmodFile, chmodDir, | | |
+| slaveId, | | |
+| sqlBefore, | | |
+| sqlInsert, | | |
+| sqlUpdate, | | |
+| sqlDelete, | | |
+| sqlAfter, | | |
+| importToTable, | | |
+| importToColumns, | | |
+| importRegion, | | |
+| importMode, | | |
+| importType, | | |
+| importNamedSheetsOnly, | | |
+| importSetReadDataOnly, | | |
+| importListSheetNames, | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| checkBoxMode | string | See `input-checkbox`_, `input-radio`_, `input-select`_ |
-| checked | string | |
-| unchecked | string | |
-| label2 | string | |
-| itemList | string | |
-| emptyHide | - | |
-| emptyItemAtStart | - | |
-| emptyItemAtEnd | - | |
-| buttonClass | string | |
+| checked | | |
+| unchecked | | |
+| label2 | | |
+| itemList | | |
+| emptyHide | | |
+| emptyItemAtStart | | |
+| emptyItemAtEnd | | |
+| buttonClass | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| dateFormat | string | yyyy-mm-dd / dd.mm.yyyy |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
@@ -2841,20 +2849,20 @@ See also at specific *FormElement* definitions.
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| extraButtonInfoClass | string | By default empty. Specify any class to be assigned to wrap extraButtonInfo |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| editor-plugins | string | See `input-editor`_ |
-| editor-toolbar | string | |
-| editor-statusbar | string | |
+| editor-plugins, | string | See `input-editor`_ |
+| editor-toolbar, | | |
+| editor-statusbar, | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| fileButtonText | string | Overwrite default 'Choose File' |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| fillStoreVar | string | Fill the STORE_VAR with custom values. See `STORE_VARS`_. |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| form | string | See `subrecord-option`_ |
-| page | string | |
-| title | string | |
-| extraDeleteForm | string | |
-| detail | string | |
-| subrecordTableClass | string | |
+| form, | string | See `subrecord-option`_ |
+| page, | | |
+| title, | | |
+| extraDeleteForm, | | |
+| detail, | | |
+| subrecordTableClass, | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| min | s/d/n | Minimum and/or maximum allowed values for input field. Can be used for numbers, dates, or strings. |
+------------------------+--------+ |
@@ -2863,12 +2871,12 @@ See also at specific *FormElement* definitions.
| processReadOnly | [n] | [0|1] By default FE's with type='readonly' are not processed during 'save'. |
| | | This option forces to process them during 'save' as well. |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| retype | string | See `input-text`_ |
-| retypeLabel | string | |
-| retypeNote | string | |
-| characterCountWrap | string | |
-| hideZero | string | |
-| emptyMeansNull | string | |
+| retype, | string | See `input-text`_ |
+| retypeLabel, | | |
+| retypeNote, | | |
+| characterCountWrap, | | |
+| hideZero, | | |
+| emptyMeansNull, | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| showSeconds | string | 0|1 - Shows the seconds on form load. Default: 0 |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
@@ -2877,10 +2885,10 @@ See also at specific *FormElement* definitions.
| timeIsOptional | string | 0|1 - Used for datetime input. 0 (default): Time is required - 1: Entering a time is optional |
| | | (defaults to 00:00:00 if none entered). |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
-| typeAheadLimit | string | See `input-typeahead`_ |
-| typeAheadMinLength | string | |
-| typeAheadSql | string | |
-| typeAheadSqlPrefetch | string | |
+| typeAheadLimit, | string | See `input-typeahead`_ |
+| typeAheadMinLength, | | |
+| typeAheadSql, | | |
+| typeAheadSqlPrefetch, | | |
+------------------------+--------+----------------------------------------------------------------------------------------------------------+
| wrapRow | string | If specified, skip default wrapping (`
`). Instead the given string is used. |
+------------------------+--------+ |
@@ -3606,16 +3614,22 @@ See also `downloadButton`_ to offer a download of an uploaded file.
(e.g. 43214), which is the serial value date in Excel. To convert such a number to a MariaDb date, use:
`DATE_ADD('1899-12-30', INTERVAL serialValue DAY)`.
- * *importToTable*: - **Required**. Providing this parameter activates the import. If the table
+ * *importToTable* = <[db.]tablename> - **Required**. Providing this parameter activates the import. If the table
doesn't exist, it will be created.
- * *importToColumns*: ,,... - If none provided, the Excel column names A, B, ... are used. Note: These
+ * *importToColumns* = ,,... - If none provided, the Excel column names A, B, ... are used. Note: These
have to match the table's column names if the table already exists.
- * *importRegion*: [tab],[startColumn],[startRow],[endColumn],[endRow]|... - All parts are optional (default:
+ * *importRegion* = [tab],[startColumn],[startRow],[endColumn],[endRow]|... - All parts are optional (default:
entire 1st sheet). Tab can either be given as an index (1-based) or a name. start/endColumn can be given either
numerically (1, 2, ...) or by column name (A, B, ...). Note that you can specify several regions to import.
- * *importMode*: `append` (default) | `replace` - The data is either appended or replace in the specified table.
- * *importType*: `auto` (default) | `xls` | `xlsx` | `ods` | `csv` - Define what kind of data should be expected by the
- Spreadsheet Reader. `auto` should work fine in most cases.
+ * *importMode* = `append` (default) | `replace` - The data is either appended or replace in the specified table.
+ * *importType* = `auto` (default) | `xls` | `xlsx` | `ods` | `csv` - Define what kind of data should be expected by the
+ Spreadsheet Reader.
+ * *importNamedSheetsOnly* = . Use this option if specific sheets cause problems
+ during import and should be skipped, by naming only those sheets, who will be read. This will also reduce the memory
+ usage.
+ * *importSetReadDataOnly* = 0|1. Read only cell data, not the cell formatting. Warning: cell types other than numerical
+ will be misinterpreted.
+ * *importListSheetNames* = 0|1. For debug use only. Will open a dialog and report all found worksheet names.
Immediately after the upload finished (before the user press save), the file will be checked on the server for it's
diff --git a/extension/Source/core/Constants.php b/extension/Source/core/Constants.php
index 870bb398f0ab0ea5be8ca96786939cd3413cd765..cacf6905eeb37379e5e3e09467b378cf5da238be 100644
--- a/extension/Source/core/Constants.php
+++ b/extension/Source/core/Constants.php
@@ -337,6 +337,10 @@ const ERROR_DND_EMPTY_REORDER_SQL = 2700;
// Form
const ERROR_FORM_RESERVED_NAME = 2800;
+// Import (Excel, ODS, ...)
+const ERROR_IMPORT_MISSING_EXPLICIT_TYPE = 2900;
+const ERROR_IMPORT_LIST_SHEET_NAMES = 2901;
+
//
// Store Names: Identifier
//
@@ -1040,6 +1044,10 @@ const FE_IMPORT_TYPE_XLS = 'xls';
const FE_IMPORT_TYPE_XLSX = 'xlsx';
const FE_IMPORT_TYPE_ODS = 'ods';
const FE_IMPORT_TYPE_CSV = 'csv';
+const FE_IMPORT_NAMED_SHEETS_ONLY = 'importNamedSheetsOnly';
+const FE_IMPORT_READ_DATA_ONLY = 'importSetReadDataOnly';
+const FE_IMPORT_LIST_SHEET_NAMES = 'importListSheetNames';
+
const FE_IMAGE_SOURCE = 'imageSource'; // Image source for a fabric element
const FE_DEFAULT_PEN_COLOR = 'defaultPenColor'; // Default pen color for a fabric element
diff --git a/extension/Source/core/Save.php b/extension/Source/core/Save.php
index bb9fba4c9880125c97163e27e69714d4e0f4ab74..9df3c42d02f44e3ecc0ba40a22722df3c2dbf8cd 100644
--- a/extension/Source/core/Save.php
+++ b/extension/Source/core/Save.php
@@ -369,7 +369,7 @@ class Save {
}
$column = $formElement[FE_NAME];
- $pathFileName = $this->doUpload($formElement, ($formValues[$column]??''), $sip, $modeUpload);
+ $pathFileName = $this->doUpload($formElement, ($formValues[$column] ?? ''), $sip, $modeUpload);
if ($modeUpload == UPLOAD_MODE_DELETEOLD && $pathFileName == '') {
$pathFileNameTmp = ''; // see '4'
@@ -661,9 +661,26 @@ class Save {
* @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
*/
private function doImport($formElement, $fileName) {
+ $importNamedSheetsOnly = array();
Support::setIfNotSet($formElement, FE_IMPORT_TYPE, FE_IMPORT_TYPE_AUTO);
+ if (!empty($formElement[FE_IMPORT_NAMED_SHEETS_ONLY])) {
+ $importNamedSheetsOnly = explode(',', $formElement[FE_IMPORT_NAMED_SHEETS_ONLY]);
+ }
+
+ // Check for keywords which needs an explicit given document type.
+ if ($formElement[FE_IMPORT_TYPE] === FE_IMPORT_TYPE_AUTO) {
+
+ $list = [FE_IMPORT_LIST_SHEET_NAMES, FE_IMPORT_READ_DATA_ONLY, FE_IMPORT_LIST_SHEET_NAMES];
+ foreach ($list as $token) {
+ if (isset($formElement[$token])) {
+ throw new UserFormException('If ' . $token .
+ ' is given, an explicit document type (like ' . FE_IMPORT_TYPE . '=xlsx) should be set.', ERROR_IMPORT_MISSING_EXPLICIT_TYPE);
+ }
+ }
+ }
+
switch ($formElement[FE_IMPORT_TYPE]) {
case FE_IMPORT_TYPE_AUTO:
$spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($fileName);
@@ -675,6 +692,23 @@ class Save {
case FE_IMPORT_TYPE_ODS:
$inputFileType = ucfirst($formElement[FE_IMPORT_TYPE]);
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);
+
+ // setReadDataOnly
+ if (($formElement[FE_IMPORT_READ_DATA_ONLY] ?? '0') != '0') {
+ $reader->setReadDataOnly(true);
+ }
+
+ // setLoadSheetsOnly
+ if (!empty ($importNamedSheetsOnly)) {
+ $reader->setLoadSheetsOnly($importNamedSheetsOnly);
+ }
+
+ if (($formElement[FE_IMPORT_LIST_SHEET_NAMES] ?? '0') != '0') {
+ $sheetNames = $reader->listWorksheetNames($fileName);
+ throw new UserFormException("Worksheets: " . implode(', ', $sheetNames),
+ ERROR_IMPORT_LIST_SHEET_NAMES);
+ }
+
$spreadsheet = $reader->load($fileName);
break;
@@ -685,7 +719,7 @@ class Save {
$tableName = $formElement[FE_IMPORT_TO_TABLE];
$regions = OnArray::trimArray(explode('|', $formElement[FE_IMPORT_REGION] ?? ''));
- $columnNames = OnArray::trimArray(explode(',', $formElement[FE_IMPORT_TO_COLUMNS]));
+ $columnNames = OnArray::trimArray(explode(',', $formElement[FE_IMPORT_TO_COLUMNS] ?? ''));
$importMode = $formElement[FE_IMPORT_MODE] ?? FE_IMPORT_MODE_APPEND;
foreach ($regions as $region) {
@@ -789,7 +823,8 @@ class Save {
* @throws UserFormException
* @throws UserReportException
*/
- private function copyUploadFile(array $formElement, array $statusUpload) {
+ private
+ function copyUploadFile(array $formElement, array $statusUpload) {
$pathFileName = '';
if (!isset($statusUpload[FILES_TMP_NAME]) || $statusUpload[FILES_TMP_NAME] === '') {
@@ -852,7 +887,8 @@ class Save {
* @throws UserFormException
* @throws UserReportException
*/
- private function autoOrient(array $formElement, $pathFileName) {
+ private
+ function autoOrient(array $formElement, $pathFileName) {
// 'autoOrient' wished?
if (!isset($formElement[FE_FILE_AUTO_ORIENT]) || $formElement[FE_FILE_AUTO_ORIENT] == '0') {
@@ -893,7 +929,8 @@ class Save {
* @throws UserFormException
* @throws UserReportException
*/
- private function splitUpload(array $formElement, $pathFileName, $chmod, array $statusUpload) {
+ private
+ function splitUpload(array $formElement, $pathFileName, $chmod, array $statusUpload) {
if (empty($formElement[FE_FILE_SPLIT]) || $statusUpload[FILES_TYPE] != MIME_TYPE_SPLIT_CAPABLE) {
return;
@@ -1016,7 +1053,8 @@ class Save {
* @throws UserFormException
* @throws UserReportException
*/
- private function doUploadSlave(array $fe, $modeUpload) {
+ private
+ function doUploadSlave(array $fe, $modeUpload) {
$sql = '';
$flagUpdateSlaveId = false;
$flagSlaveDeleted = false;
diff --git a/extension/Source/core/sample.xlsx b/extension/Source/core/sample.xlsx
deleted file mode 100644
index bbcfe0f7bb0c9217fb6114a4177af6cc83fec086..0000000000000000000000000000000000000000
Binary files a/extension/Source/core/sample.xlsx and /dev/null differ