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 (`<div class='col-md-?'>`). 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*: <mariadb.tablename> - **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*: <col1>,<col2>,... - If none provided, the Excel column names A, B, ... are used. Note: These + * *importToColumns* = <col1>,<col2>,... - 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* = <comma separated list of sheet names>. 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..a578d4a75b4f9ddc769a0d598a1e5a2a0c8fcb40 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,25 @@ 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]); + } + + 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 +691,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 +718,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 +822,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 +886,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 +928,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 +1052,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