diff --git a/composer.json b/composer.json index 8f4e849a5f..27dc854f35 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "winter/wn-backend-module": "dev-develop", "winter/wn-cms-module": "dev-develop", "laravel/framework": "^9.1", - "wikimedia/composer-merge-plugin": "~2.1.0" + "wikimedia/composer-merge-plugin": "~2.1.0", + "phpoffice/phpspreadsheet": "^2.1|^3.6" }, "require-dev": { "phpunit/phpunit": "^9.5.8", diff --git a/modules/backend/behaviors/ImportExportController.php b/modules/backend/behaviors/ImportExportController.php index 057a0758a3..a94e9c67fd 100644 --- a/modules/backend/behaviors/ImportExportController.php +++ b/modules/backend/behaviors/ImportExportController.php @@ -1,21 +1,23 @@ $this->getImportFileColumnsFromCsv($path), + 'excel' => $this->getImportFileColumnsFromSpreadsheet($path), + default => throw new ApplicationException('Unsupported file format'), + }; + } + + protected function getImportFileColumnsFromCsv(string $path) + { $reader = $this->createCsvReader($path); $firstRow = $reader->fetchOne(0); @@ -343,6 +356,20 @@ protected function getImportFileColumns() return $firstRow; } + protected function getImportFileColumnsFromSpreadsheet(string $path) + { + $spreadsheet = IOFactory::load($path, IReader::READ_DATA_ONLY, [ + IOFactory::READER_XLSX, + IOFactory::READER_XLS, + IOFactory::READER_ODS, + ]); + + $sheet = $spreadsheet->getActiveSheet(); + $data = $sheet->toArray(); + + return $data[0]; + } + /** * Get the index offset to add to the reported row number in status messages * diff --git a/modules/backend/behaviors/importexportcontroller/partials/fields_import.yaml b/modules/backend/behaviors/importexportcontroller/partials/fields_import.yaml index 139a368cb3..e0377512dd 100644 --- a/modules/backend/behaviors/importexportcontroller/partials/fields_import.yaml +++ b/modules/backend/behaviors/importexportcontroller/partials/fields_import.yaml @@ -12,7 +12,7 @@ fields: type: fileupload mode: file span: left - fileTypes: csv + fileTypes: csv, xls, xlsx, ods useCaption: false format_preset: @@ -22,6 +22,7 @@ fields: options: standard: backend::lang.import_export.standard_format custom: backend::lang.import_export.custom_format + excel: backend::lang.import_export.excel_format span: right format_delimiter: @@ -92,4 +93,4 @@ fields: step3_section: label: backend::lang.import_export.set_import_options - type: section \ No newline at end of file + type: section diff --git a/modules/backend/lang/en/lang.php b/modules/backend/lang/en/lang.php index 5c1a96f55b..dfafe63912 100644 --- a/modules/backend/lang/en/lang.php +++ b/modules/backend/lang/en/lang.php @@ -529,8 +529,9 @@ 'set_import_options' => '3. Set import options', 'export_output_format' => '1. Export output format', 'file_format' => 'File format', - 'standard_format' => 'Standard format', - 'custom_format' => 'Custom format', + 'standard_format' => 'CSV Standard', + 'custom_format' => 'CSV Custom', + 'excel_format' => 'Excel (xls, xlsx, ods)', 'delimiter_char' => 'Delimiter character', 'enclosure_char' => 'Enclosure character', 'escape_char' => 'Escape character', diff --git a/modules/backend/models/ImportModel.php b/modules/backend/models/ImportModel.php index 8552d08b4d..ca0fc1d3ec 100644 --- a/modules/backend/models/ImportModel.php +++ b/modules/backend/models/ImportModel.php @@ -1,11 +1,14 @@ $this->processImportDataAsCsv($filePath, $matches, $options), + 'excel' => $this->processImportDataFromSpreadsheet($filePath, $matches, $options), + default => throw new ApplicationException('Unsupported file format'), + }; + } + + protected function processImportDataAsCsv(string $filePath, array $matches, array $options): array + { /* * Read CSV */ @@ -153,6 +167,29 @@ protected function processImportData($filePath, $matches, $options) return $result; } + protected function processImportDataFromSpreadsheet(string $filePath, array $matches, array $options): array + { + $spreadsheet = IOFactory::load($filePath, IReader::READ_DATA_ONLY, [ + IOFactory::READER_XLSX, + IOFactory::READER_XLS, + IOFactory::READER_ODS, + ]); + + $sheet = $spreadsheet->getActiveSheet(); + + $sheetData = $sheet->toArray(); + if ($options['firstRowTitles']) { + $sheetData = array_slice($sheetData, 1); + } + + $result = []; + foreach ($sheetData as $rowData) { + $result[] = $this->processImportRow($rowData, $matches); + } + + return $result; + } + /** * Converts a single row of CSV data to the column map. * @return array