From b9f3cb52420c514eb6cc02ef0885da98e77d089e Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2024 15:42:03 -0500 Subject: [PATCH 1/5] Add Excel Format to Import Controller --- composer.json | 3 +- .../behaviors/ImportExportController.php | 52 ++++++++++++++----- .../partials/fields_import.yaml | 5 +- modules/backend/lang/en/lang.php | 5 +- modules/backend/models/ImportModel.php | 44 ++++++++++++++-- 5 files changed, 89 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index 8f4e849a5f..606d19d6dd 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": "^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..bdedda59d5 100644 --- a/modules/backend/behaviors/ImportExportController.php +++ b/modules/backend/behaviors/ImportExportController.php @@ -1,21 +1,24 @@ getImportFilePath()) { + if (! $path = $this->getImportFilePath()) { return null; } + $fileFormat = post('format_preset'); + + return match ($fileFormat) { + 'standard', 'custom' => $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 +357,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..91535e5cc3 100644 --- a/modules/backend/models/ImportModel.php +++ b/modules/backend/models/ImportModel.php @@ -1,11 +1,15 @@ $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 +168,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 From 9d9a01f59585be989e7c8af372b9fc82ed7f2c53 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2024 15:57:13 -0500 Subject: [PATCH 2/5] remove extra empty line --- modules/backend/behaviors/ImportExportController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/backend/behaviors/ImportExportController.php b/modules/backend/behaviors/ImportExportController.php index bdedda59d5..d1e239240d 100644 --- a/modules/backend/behaviors/ImportExportController.php +++ b/modules/backend/behaviors/ImportExportController.php @@ -19,7 +19,6 @@ use SplTempFileObject; use Winter\Storm\Support\Str; - /** * Adds features for importing and exporting data. * From 1fcd4c2b8ddcb52e3e572ea5daa3ddf21926f8d0 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2024 15:58:05 -0500 Subject: [PATCH 3/5] lower minimal phpoffice version for php 8.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 606d19d6dd..27dc854f35 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "winter/wn-cms-module": "dev-develop", "laravel/framework": "^9.1", "wikimedia/composer-merge-plugin": "~2.1.0", - "phpoffice/phpspreadsheet": "^3.6" + "phpoffice/phpspreadsheet": "^2.1|^3.6" }, "require-dev": { "phpunit/phpunit": "^9.5.8", From a437f5ac48cc44a6f8b6cf60428f95ec164085bd Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2024 15:59:16 -0500 Subject: [PATCH 4/5] remove extra empty line --- modules/backend/models/ImportModel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/backend/models/ImportModel.php b/modules/backend/models/ImportModel.php index 91535e5cc3..ca0fc1d3ec 100644 --- a/modules/backend/models/ImportModel.php +++ b/modules/backend/models/ImportModel.php @@ -10,7 +10,6 @@ use Winter\Storm\Support\Facades\File; use Winter\Storm\Support\Str; - /** * Model used for importing data * From fd451ac09059fe04a5f78978dd1b84e2afb54a77 Mon Sep 17 00:00:00 2001 From: Marc Jauvin Date: Sat, 21 Dec 2024 16:07:49 -0500 Subject: [PATCH 5/5] remove extra space --- modules/backend/behaviors/ImportExportController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/backend/behaviors/ImportExportController.php b/modules/backend/behaviors/ImportExportController.php index d1e239240d..a94e9c67fd 100644 --- a/modules/backend/behaviors/ImportExportController.php +++ b/modules/backend/behaviors/ImportExportController.php @@ -322,7 +322,7 @@ protected function getImportDbColumns() protected function getImportFileColumns() { - if (! $path = $this->getImportFilePath()) { + if (!$path = $this->getImportFilePath()) { return null; }