From 571651873fd6ebaeea61e872860739deb0ea0ad8 Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Thu, 21 Jul 2022 17:58:02 +0200 Subject: [PATCH 1/8] feat: add data endpoint to export as excel file --- packages/nocodb-sdk/src/lib/globals.ts | 1 + packages/nocodb/package-lock.json | 142 +++++++++++++++++- packages/nocodb/package.json | 5 +- .../meta/api/dataApis/dataAliasExportApis.ts | 33 ++++ .../src/lib/meta/api/dataApis/helpers.ts | 70 +++++++++ packages/nocodb/src/lib/utils/projectAcl.ts | 3 + 6 files changed, 251 insertions(+), 3 deletions(-) diff --git a/packages/nocodb-sdk/src/lib/globals.ts b/packages/nocodb-sdk/src/lib/globals.ts index 8c990d585e..b63424b42a 100644 --- a/packages/nocodb-sdk/src/lib/globals.ts +++ b/packages/nocodb-sdk/src/lib/globals.ts @@ -12,6 +12,7 @@ export enum RelationTypes { } export enum ExportTypes { + EXCEL = 'excel', CSV = 'csv', } diff --git a/packages/nocodb/package-lock.json b/packages/nocodb/package-lock.json index 464f750569..c7b690811c 100644 --- a/packages/nocodb/package-lock.json +++ b/packages/nocodb/package-lock.json @@ -100,7 +100,8 @@ "unique-names-generator": "^4.3.1", "uuid": "^8.2.0", "validator": "^13.1.1", - "xc-core-ts": "^0.1.0" + "xc-core-ts": "^0.1.0", + "xlsx": "^0.18.5" }, "devDependencies": { "@bitjson/npm-scripts-info": "^1.0.0", @@ -2501,6 +2502,14 @@ "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4387,6 +4396,18 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "optional": true }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -4926,6 +4947,14 @@ "node": ">=10" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -9887,6 +9916,14 @@ "node": ">= 0.6" } }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -19930,6 +19967,17 @@ "node": ">= 0.6" } }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -24426,6 +24474,22 @@ "node": ">= 6.4.0" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -24582,6 +24646,26 @@ "node": ">=0.10.0" } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", @@ -26624,6 +26708,11 @@ "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", "dev": true }, + "adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==" + }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -28156,6 +28245,15 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "optional": true }, + "cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "requires": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + } + }, "chai": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", @@ -28569,6 +28667,11 @@ } } }, + "codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -32481,6 +32584,11 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, + "frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -40312,6 +40420,14 @@ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" }, + "ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "requires": { + "frac": "~1.1.2" + } + }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -43869,6 +43985,16 @@ "triple-beam": "^1.3.0" } }, + "wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" + }, + "word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -43985,6 +44111,20 @@ } } }, + "xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "requires": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + } + }, "xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", diff --git a/packages/nocodb/package.json b/packages/nocodb/package.json index d436b04bb1..147e41b876 100644 --- a/packages/nocodb/package.json +++ b/packages/nocodb/package.json @@ -186,7 +186,8 @@ "unique-names-generator": "^4.3.1", "uuid": "^8.2.0", "validator": "^13.1.1", - "xc-core-ts": "^0.1.0" + "xc-core-ts": "^0.1.0", + "xlsx": "^0.18.5" }, "devDependencies": { "@bitjson/npm-scripts-info": "^1.0.0", @@ -263,4 +264,4 @@ "**/*.spec.js" ] } -} \ No newline at end of file +} diff --git a/packages/nocodb/src/lib/meta/api/dataApis/dataAliasExportApis.ts b/packages/nocodb/src/lib/meta/api/dataApis/dataAliasExportApis.ts index 7e18477427..5c393ced04 100644 --- a/packages/nocodb/src/lib/meta/api/dataApis/dataAliasExportApis.ts +++ b/packages/nocodb/src/lib/meta/api/dataApis/dataAliasExportApis.ts @@ -1,12 +1,35 @@ import { Request, Response, Router } from 'express'; +import * as XLSX from 'xlsx'; import ncMetaAclMw from '../../helpers/ncMetaAclMw'; import { extractCsvData, + extractXlsxData, getViewAndModelFromRequestByAliasOrId, } from './helpers'; import apiMetrics from '../../helpers/apiMetrics'; import View from '../../../models/View'; +async function excelDataExport(req: Request, res: Response) { + const {model, view} = await getViewAndModelFromRequestByAliasOrId(req); + let targetView = view; + if (!targetView) { + targetView = await View.getDefaultView(model.id); + } + const { offset, elapsed, data } = await extractXlsxData(targetView, req); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, data, targetView.title); + const buf = XLSX.write(wb, { type: "base64", bookType: "xlsx" }); + res.set({ + 'Access-Control-Expose-Headers': 'nc-export-offset', + 'nc-export-offset': offset, + 'nc-export-elapsed-time': elapsed, + 'Content-Disposition': `attachment; filename="${encodeURI( + targetView.title + )}-export.xlsx"`, + }); + res.end(buf); +} + async function csvDataExport(req: Request, res: Response) { const { model, view } = await getViewAndModelFromRequestByAliasOrId(req); let targetView = view; @@ -38,5 +61,15 @@ router.get( apiMetrics, ncMetaAclMw(csvDataExport, 'exportCsv') ); +router.get( + '/api/v1/db/data/:orgs/:projectName/:tableName/export/excel', + apiMetrics, + ncMetaAclMw(excelDataExport, 'exportExcel') +); +router.get( + '/api/v1/db/data/:orgs/:projectName/:tableName/views/:viewName/export/excel', + apiMetrics, + ncMetaAclMw(excelDataExport, 'exportExcel') +); export default router; diff --git a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts index 27d93d356e..19952ee9a1 100644 --- a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts +++ b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts @@ -8,6 +8,7 @@ import NcConnectionMgrv2 from '../../../utils/common/NcConnectionMgrv2'; import { isSystemColumn, UITypes } from 'nocodb-sdk'; import { nocoExecute } from 'nc-help'; +import * as XLSX from 'xlsx'; import Column from '../../../models/Column'; import LookupColumn from '../../../models/LookupColumn'; import LinkToAnotherRecordColumn from '../../../models/LinkToAnotherRecordColumn'; @@ -36,6 +37,75 @@ export async function getViewAndModelFromRequestByAliasOrId( return { model, view }; } +export async function extractXlsxData(view: View, req: Request) { + const base = await Base.get(view.base_id); + + await view.getModelWithInfo(); + await view.getColumns(); + + view.model.columns = view.columns + .filter((c) => c.show) + .map( + (c) => + new Column({ ...c, ...view.model.columnsById[c.fk_column_id] } as any) + ) + .filter((column) => !isSystemColumn(column) || view.show_system_fields); + + const baseModel = await Model.getBaseModelSQL({ + id: view.model.id, + viewId: view?.id, + dbDriver: NcConnectionMgrv2.get(base), + }); + + let offset = +req.query.offset || 0; + const limit = 100; + // const size = +process.env.NC_EXPORT_MAX_SIZE || 1024; + const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000; + const csvRows = []; + const startTime = process.hrtime(); + let elapsed, temp; + + for ( + elapsed = 0; + elapsed < timeout; + offset += limit, + temp = process.hrtime(startTime), + elapsed = temp[0] * 1000 + temp[1] / 1000000 + ) { + const rows = await nocoExecute( + await getAst({ + query: req.query, + includePkByDefault: false, + model: view.model, + view, + }), + await baseModel.list({ ...req.query, offset, limit }), + {}, + req.query + ); + + if (!rows?.length) { + offset = -1; + break; + } + + for (const row of rows) { + const csvRow = { ...row }; + + for (const column of view.model.columns) { + if (isSystemColumn(column) && !view.show_system_fields) continue; + csvRow[column.title] = await serializeCellValue({ + value: row[column.title], + column, + }); + } + csvRows.push(csvRow); + } + } + const data = XLSX.utils.json_to_sheet(csvRows); + return { offset, csvRows, elapsed, data }; +} + export async function extractCsvData(view: View, req: Request) { const base = await Base.get(view.base_id); diff --git a/packages/nocodb/src/lib/utils/projectAcl.ts b/packages/nocodb/src/lib/utils/projectAcl.ts index ceb9fcd034..a3482a7137 100644 --- a/packages/nocodb/src/lib/utils/projectAcl.ts +++ b/packages/nocodb/src/lib/utils/projectAcl.ts @@ -27,6 +27,7 @@ export default { dataGroupBy: true, commentsCount: true, exportCsv: true, + exportExcel: true, viewList: true, columnList: true, @@ -142,6 +143,7 @@ export default { // project projectGet: true, exportCsv: true, + exportExcel: true, //table tableGet: true, @@ -205,6 +207,7 @@ export default { dataGroupBy: true, commentsCount: true, exportCsv: true, + exportExcel: true, // sort & filter sortList: true, From a18d5fa330a900ea9c3e625507c4abbb3038b1b0 Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Thu, 21 Jul 2022 18:00:26 +0200 Subject: [PATCH 2/8] feat: add interface to export excel file --- .../spreadsheet/components/MoreActions.vue | 36 +++++++++++++++++++ packages/nc-gui/package-lock.json | 2 +- packages/nc-gui/package.json | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue index c730db9227..549e0232b0 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue @@ -28,6 +28,15 @@ + + + mdi-download-outline + + + {{ $t('activity.downloadExcel') }} + + + import FileSaver from 'file-saver'; +import * as XLSX from 'xlsx'; import { ExportTypes } from 'nocodb-sdk'; import DropOrSelectFileModal from '~/components/import/DropOrSelectFileModal'; import ColumnMappingModal from '~/components/project/spreadsheet/components/importExport/ColumnMappingModal'; @@ -220,6 +230,32 @@ export default { }) ); }, + async exportExcel() { + let offset = 0; + let c = 1; + const res = await this.$api.dbViewRow.export( + 'noco', + this.projectName, + this.meta.title, + this.selectedView.title, + ExportTypes.EXCEL, + { + responseType: 'base64', + query: { + offset, + }, + } + ); + const workbook = XLSX.read(res.data, { type: 'base64' }); + XLSX.writeFile(workbook, `${this.meta.title}_exported_${c++}.xlsx`); + + offset = +res.headers['nc-export-offset']; + if (offset > -1) { + this.$toast.info('Downloading more files').goAway(3000); + } else { + this.$toast.success('Successfully exported all table data').goAway(3000); + } + }, async exportCsv() { let offset = 0; let c = 1; diff --git a/packages/nc-gui/package-lock.json b/packages/nc-gui/package-lock.json index c6772c1483..47a28b25e5 100644 --- a/packages/nc-gui/package-lock.json +++ b/packages/nc-gui/package-lock.json @@ -56,7 +56,7 @@ "vuelidate": "^0.7.6", "vuetify-datetime-picker": "^2.1.1", "vuex-persistedstate": "^3.1.0", - "xlsx": "^0.17.3", + "xlsx": "^0.17.5", "xterm": "^4.8.1", "xterm-addon-fit": "^0.4.0", "xterm-addon-web-links": "^0.4.0" diff --git a/packages/nc-gui/package.json b/packages/nc-gui/package.json index 56a8b4fe42..be6c6cf232 100644 --- a/packages/nc-gui/package.json +++ b/packages/nc-gui/package.json @@ -60,7 +60,7 @@ "vuelidate": "^0.7.6", "vuetify-datetime-picker": "^2.1.1", "vuex-persistedstate": "^3.1.0", - "xlsx": "^0.17.3", + "xlsx": "^0.17.5", "xterm": "^4.8.1", "xterm-addon-fit": "^0.4.0", "xterm-addon-web-links": "^0.4.0" From f4cca91d13affb7f310b5ec47f2d35eb668ad650 Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Wed, 27 Jul 2022 12:13:16 +0200 Subject: [PATCH 3/8] feat: add export excel on public views --- .../spreadsheet/components/MoreActions.vue | 75 ++++++++---- .../src/lib/meta/api/dataApis/helpers.ts | 86 ++++---------- .../api/publicApis/publicDataExportApis.ts | 109 ++++++++++++------ 3 files changed, 153 insertions(+), 117 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue index 549e0232b0..6e6ec7df43 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue @@ -233,27 +233,62 @@ export default { async exportExcel() { let offset = 0; let c = 1; - const res = await this.$api.dbViewRow.export( - 'noco', - this.projectName, - this.meta.title, - this.selectedView.title, - ExportTypes.EXCEL, - { - responseType: 'base64', - query: { - offset, - }, - } - ); - const workbook = XLSX.read(res.data, { type: 'base64' }); - XLSX.writeFile(workbook, `${this.meta.title}_exported_${c++}.xlsx`); + try { + while (!isNaN(offset) && offset > -1) { + let res; + if (this.publicViewId) { + console.log('IF', this.publicViewId) + res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.EXCEL, { + responseType: 'blob', + query: { + fields: + this.queryParams && + this.queryParams.fieldsOrder && + this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]), + offset, + sortArrJson: JSON.stringify( + this.reqPayload && + this.reqPayload.sorts && + this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({ + direction, + fk_column_id, + })) + ), + filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters), + }, + headers: { + 'xc-password': this.reqPayload && this.reqPayload.password, + }, + }); + } else { + console.log('ELSE') + res = await this.$api.dbViewRow.export( + 'noco', + this.projectName, + this.meta.title, + this.selectedView.title, + ExportTypes.EXCEL, + { + responseType: 'base64', + query: { + offset, + }, + } + ); + } + const workbook = XLSX.read(res.data, { type: 'base64' }); + XLSX.writeFile(workbook, `${this.meta.title}_exported_${c++}.xlsx`); - offset = +res.headers['nc-export-offset']; - if (offset > -1) { - this.$toast.info('Downloading more files').goAway(3000); - } else { - this.$toast.success('Successfully exported all table data').goAway(3000); + offset = +res.headers['nc-export-offset']; + if (offset > -1) { + this.$toast.info('Downloading more files').goAway(3000); + } else { + this.$toast.success('Successfully exported all table data').goAway(3000); + } + } + } catch (e) { + console.log(e); + this.$toast.error(e.message).goAway(3000); } }, async exportCsv() { diff --git a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts index 19952ee9a1..968f062265 100644 --- a/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts +++ b/packages/nocodb/src/lib/meta/api/dataApis/helpers.ts @@ -57,53 +57,10 @@ export async function extractXlsxData(view: View, req: Request) { dbDriver: NcConnectionMgrv2.get(base), }); - let offset = +req.query.offset || 0; - const limit = 100; - // const size = +process.env.NC_EXPORT_MAX_SIZE || 1024; - const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000; - const csvRows = []; - const startTime = process.hrtime(); - let elapsed, temp; - - for ( - elapsed = 0; - elapsed < timeout; - offset += limit, - temp = process.hrtime(startTime), - elapsed = temp[0] * 1000 + temp[1] / 1000000 - ) { - const rows = await nocoExecute( - await getAst({ - query: req.query, - includePkByDefault: false, - model: view.model, - view, - }), - await baseModel.list({ ...req.query, offset, limit }), - {}, - req.query - ); - - if (!rows?.length) { - offset = -1; - break; - } - - for (const row of rows) { - const csvRow = { ...row }; + const { offset, dbRows, elapsed } = await getDbRows(baseModel, view, req); + const data = XLSX.utils.json_to_sheet(dbRows); - for (const column of view.model.columns) { - if (isSystemColumn(column) && !view.show_system_fields) continue; - csvRow[column.title] = await serializeCellValue({ - value: row[column.title], - column, - }); - } - csvRows.push(csvRow); - } - } - const data = XLSX.utils.json_to_sheet(csvRows); - return { offset, csvRows, elapsed, data }; + return { offset, dbRows, elapsed, data }; } export async function extractCsvData(view: View, req: Request) { @@ -126,11 +83,27 @@ export async function extractCsvData(view: View, req: Request) { dbDriver: NcConnectionMgrv2.get(base), }); + const { offset, dbRows, elapsed } = await getDbRows(baseModel, view, req); + + const data = papaparse.unparse( + { + fields: view.model.columns.map((c) => c.title), + data: dbRows, + }, + { + escapeFormulae: true, + } + ); + + return { offset, dbRows, elapsed, data }; +} + +async function getDbRows(baseModel, view: View, req: Request) { let offset = +req.query.offset || 0; const limit = 100; // const size = +process.env.NC_EXPORT_MAX_SIZE || 1024; const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000; - const csvRows = []; + const dbRows = []; const startTime = process.hrtime(); let elapsed, temp; @@ -159,30 +132,19 @@ export async function extractCsvData(view: View, req: Request) { } for (const row of rows) { - const csvRow = { ...row }; + const dbRow = { ...row }; for (const column of view.model.columns) { if (isSystemColumn(column) && !view.show_system_fields) continue; - csvRow[column.title] = await serializeCellValue({ + dbRow[column.title] = await serializeCellValue({ value: row[column.title], column, }); } - csvRows.push(csvRow); + dbRows.push(dbRow); } } - - const data = papaparse.unparse( - { - fields: view.model.columns.map((c) => c.title), - data: csvRows, - }, - { - escapeFormulae: true, - } - ); - - return { offset, csvRows, elapsed, data }; + return { offset, dbRows, elapsed }; } export async function serializeCellValue({ diff --git a/packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts b/packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts index 49ecdf394a..f5ed20d574 100644 --- a/packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts +++ b/packages/nocodb/src/lib/meta/api/publicApis/publicDataExportApis.ts @@ -1,4 +1,5 @@ import { Request, Response, Router } from 'express'; +import * as XLSX from 'xlsx'; import View from '../../../models/View'; import Model from '../../../models/Model'; import Base from '../../../models/Base'; @@ -12,8 +13,38 @@ import LookupColumn from '../../../models/LookupColumn'; import catchError, { NcError } from '../../helpers/catchError'; import getAst from '../../../db/sql-data-mapper/lib/sql/helpers/getAst'; +async function exportExcel(req: Request, res: Response) { + const view = await View.getByUUID(req.params.publicDataUuid); + if (!view) NcError.notFound('Not found'); + if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); + + if (view.password && view.password !== req.headers?.['xc-password']) { + NcError.forbidden(ErrorMessages.INVALID_SHARED_VIEW_PASSWORD); + } + + const model = await view.getModelWithInfo(); + await view.getColumns(); + + const { offset, dbRows, elapsed } = await getDbRows(model, view, req); + + const data = XLSX.utils.json_to_sheet(dbRows); + const wb = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(wb, data, view.title); + const buf = XLSX.write(wb, { type: "base64", bookType: "xlsx" }); + res.set({ + 'Access-Control-Expose-Headers': 'nc-export-offset', + 'nc-export-offset': offset, + 'nc-export-elapsed-time': elapsed, + 'Content-Disposition': `attachment; filename="${encodeURI( + view.title + )}-export.xlsx"`, + }); + res.end(buf); +} + async function exportCsv(req: Request, res: Response) { const view = await View.getByUUID(req.params.publicDataUuid); + const fields = req.query.fields; if (!view) NcError.notFound('Not found'); if (view.type !== ViewTypes.GRID) NcError.notFound('Not found'); @@ -25,6 +56,40 @@ async function exportCsv(req: Request, res: Response) { const model = await view.getModelWithInfo(); await view.getColumns(); + const { offset, dbRows, elapsed } = await getDbRows(model, view, req); + + const data = papaparse.unparse( + { + fields: model.columns + .sort((c1, c2) => + Array.isArray(fields) + ? fields.indexOf(c1.title as any) - fields.indexOf(c2.title as any) + : 0 + ) + .filter( + (c) => + !fields || !Array.isArray(fields) || fields.includes(c.title as any) + ) + .map((c) => c.title), + data: dbRows, + }, + { + escapeFormulae: true, + } + ); + + res.set({ + 'Access-Control-Expose-Headers': 'nc-export-offset', + 'nc-export-offset': offset, + 'nc-export-elapsed-time': elapsed, + 'Content-Disposition': `attachment; filename="${encodeURI( + view.title + )}-export.csv"`, + }); + res.send(data); +} + +async function getDbRows(model, view: View, req: Request) { view.model.columns = view.columns .filter((c) => c.show) .map( @@ -35,7 +100,6 @@ async function exportCsv(req: Request, res: Response) { if (!model) NcError.notFound('Table not found'); - const fields = req.query.fields; const listArgs: any = { ...req.query }; try { listArgs.filterArr = JSON.parse(listArgs.filterArrJson); @@ -62,7 +126,7 @@ async function exportCsv(req: Request, res: Response) { const limit = 100; // const size = +process.env.NC_EXPORT_MAX_SIZE || 1024; const timeout = +process.env.NC_EXPORT_MAX_TIMEOUT || 5000; - const csvRows = []; + const dbRows = []; const startTime = process.hrtime(); let elapsed, temp; @@ -86,47 +150,18 @@ async function exportCsv(req: Request, res: Response) { } for (const row of rows) { - const csvRow = { ...row }; + const dbRow = { ...row }; for (const column of view.model.columns) { - csvRow[column.title] = await serializeCellValue({ + dbRow[column.title] = await serializeCellValue({ value: row[column.title], column, }); } - csvRows.push(csvRow); + dbRows.push(dbRow); } } - - const data = papaparse.unparse( - { - fields: model.columns - .sort((c1, c2) => - Array.isArray(fields) - ? fields.indexOf(c1.title as any) - fields.indexOf(c2.title as any) - : 0 - ) - .filter( - (c) => - !fields || !Array.isArray(fields) || fields.includes(c.title as any) - ) - .map((c) => c.title), - data: csvRows, - }, - { - escapeFormulae: true, - } - ); - - res.set({ - 'Access-Control-Expose-Headers': 'nc-export-offset', - 'nc-export-offset': offset, - 'nc-export-elapsed-time': elapsed, - 'Content-Disposition': `attachment; filename="${encodeURI( - view.title - )}-export.csv"`, - }); - res.send(data); + return { offset, dbRows, elapsed }; } async function serializeCellValue({ @@ -198,4 +233,8 @@ router.get( '/api/v1/db/public/shared-view/:publicDataUuid/rows/export/csv', catchError(exportCsv) ); +router.get( + '/api/v1/db/public/shared-view/:publicDataUuid/rows/export/excel', + catchError(exportExcel) +); export default router; From 68204a860395522279ce4b522498508c17773505 Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Wed, 27 Jul 2022 12:19:02 +0200 Subject: [PATCH 4/8] feat: add translation --- packages/nc-gui/lang/da.json | 1 + packages/nc-gui/lang/de.json | 1 + packages/nc-gui/lang/en.json | 1 + packages/nc-gui/lang/es.json | 1 + packages/nc-gui/lang/fa.json | 1 + packages/nc-gui/lang/fi.json | 1 + packages/nc-gui/lang/fr.json | 1 + packages/nc-gui/lang/hr.json | 1 + packages/nc-gui/lang/id.json | 1 + packages/nc-gui/lang/it_IT.json | 1 + packages/nc-gui/lang/iw.json | 1 + packages/nc-gui/lang/ja.json | 1 + packages/nc-gui/lang/ko.json | 1 + packages/nc-gui/lang/lv.json | 1 + packages/nc-gui/lang/nl.json | 1 + packages/nc-gui/lang/no.json | 1 + packages/nc-gui/lang/pl.json | 1 + packages/nc-gui/lang/pt.json | 1 + packages/nc-gui/lang/pt_BR.json | 1 + packages/nc-gui/lang/ru.json | 1 + packages/nc-gui/lang/sl.json | 1 + packages/nc-gui/lang/sv.json | 1 + packages/nc-gui/lang/th.json | 1 + packages/nc-gui/lang/tr.json | 1 + packages/nc-gui/lang/uk.json | 1 + packages/nc-gui/lang/vi.json | 1 + packages/nc-gui/lang/zh_CN.json | 1 + packages/nc-gui/lang/zh_HK.json | 1 + packages/nc-gui/lang/zh_TW.json | 3 ++- 29 files changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/nc-gui/lang/da.json b/packages/nc-gui/lang/da.json index ac48c94ae5..76d047bbba 100644 --- a/packages/nc-gui/lang/da.json +++ b/packages/nc-gui/lang/da.json @@ -309,6 +309,7 @@ "importExcel": "Import Excel.", "importCSV": "Import CSV.", "downloadCSV": "Download som CSV.", + "downloadExcel": "Download som XLSX.", "uploadCSV": "Upload CSV.", "import": "Importere", "importMetadata": "Import metadata.", diff --git a/packages/nc-gui/lang/de.json b/packages/nc-gui/lang/de.json index a4e332aa4d..d8b38a3e20 100644 --- a/packages/nc-gui/lang/de.json +++ b/packages/nc-gui/lang/de.json @@ -309,6 +309,7 @@ "importExcel": "Import Excel", "importCSV": "Import CSV", "downloadCSV": "Download als CSV", + "downloadExcel": "Download als XLSX", "uploadCSV": "Hochladen CSV", "import": "Importieren", "importMetadata": "Metadaten importieren", diff --git a/packages/nc-gui/lang/en.json b/packages/nc-gui/lang/en.json index 363d371b3a..609f17d494 100644 --- a/packages/nc-gui/lang/en.json +++ b/packages/nc-gui/lang/en.json @@ -309,6 +309,7 @@ "importExcel": "Import Excel", "importCSV": "Import CSV", "downloadCSV": "Download as CSV", + "downloadExcel": "Download as XLSX", "uploadCSV": "Upload CSV", "import": "Import", "importMetadata": "Import Metadata", diff --git a/packages/nc-gui/lang/es.json b/packages/nc-gui/lang/es.json index 22a6e992ab..6fdb219a4b 100644 --- a/packages/nc-gui/lang/es.json +++ b/packages/nc-gui/lang/es.json @@ -309,6 +309,7 @@ "importExcel": "Importar Excel", "importCSV": "Import CSV", "downloadCSV": "Descargar como CSV", + "downloadExcel": "Descargar como XLSX", "uploadCSV": "Subir CSV", "import": "Importar", "importMetadata": "Importar metadatos", diff --git a/packages/nc-gui/lang/fa.json b/packages/nc-gui/lang/fa.json index 335e034da4..0e564a1a07 100644 --- a/packages/nc-gui/lang/fa.json +++ b/packages/nc-gui/lang/fa.json @@ -309,6 +309,7 @@ "importExcel": "وارد کردن فایل Excel", "importCSV": "Import CSV", "downloadCSV": "دانلود به‌عنوان CSV", + "downloadXLSX": "دانلود به‌عنوان XLSX", "uploadCSV": "بارگذاری CSV", "import": "وارد کردن", "importMetadata": "وارد کردن فراداده", diff --git a/packages/nc-gui/lang/fi.json b/packages/nc-gui/lang/fi.json index 035045e498..8ca1bf26d3 100644 --- a/packages/nc-gui/lang/fi.json +++ b/packages/nc-gui/lang/fi.json @@ -309,6 +309,7 @@ "importExcel": "Tuonti excel", "importCSV": "Import CSV", "downloadCSV": "Lataa CSV", + "downloadXLSX": "Lataa XLSX", "uploadCSV": "Lataa CSV", "import": "Tuonti", "importMetadata": "Tuo metatieto", diff --git a/packages/nc-gui/lang/fr.json b/packages/nc-gui/lang/fr.json index 9663597c8d..d319b68822 100644 --- a/packages/nc-gui/lang/fr.json +++ b/packages/nc-gui/lang/fr.json @@ -309,6 +309,7 @@ "importExcel": "Importer depuis Excel", "importCSV": "Import CSV", "downloadCSV": "Télécharger comme CSV", + "downloadExcel": "Télécharger comme XLSX", "uploadCSV": "Téléverser un CSV", "import": "Importer", "importMetadata": "Importer les métadonnées", diff --git a/packages/nc-gui/lang/hr.json b/packages/nc-gui/lang/hr.json index 5be380f930..cad1fea145 100644 --- a/packages/nc-gui/lang/hr.json +++ b/packages/nc-gui/lang/hr.json @@ -309,6 +309,7 @@ "importExcel": "Uvoz Excel", "importCSV": "Import CSV", "downloadCSV": "Preuzmite kao CSV", + "downloadExcel": "Preuzmite kao XLSX", "uploadCSV": "Prenesite CSV", "import": "Uvoz", "importMetadata": "Uvoz metapodataka", diff --git a/packages/nc-gui/lang/id.json b/packages/nc-gui/lang/id.json index 4704e039ca..0c133f39b1 100644 --- a/packages/nc-gui/lang/id.json +++ b/packages/nc-gui/lang/id.json @@ -309,6 +309,7 @@ "importExcel": "Impor Excel.", "importCSV": "Import CSV", "downloadCSV": "Unduh sebagai CSV.", + "downloadExcel": "Unduh sebagai XLSX.", "uploadCSV": "Unggah CSV.", "import": "Impor", "importMetadata": "Impor Metadata.", diff --git a/packages/nc-gui/lang/it_IT.json b/packages/nc-gui/lang/it_IT.json index 36e925f1b9..fd92653acb 100644 --- a/packages/nc-gui/lang/it_IT.json +++ b/packages/nc-gui/lang/it_IT.json @@ -309,6 +309,7 @@ "importExcel": "Importa Excel.", "importCSV": "Import CSV", "downloadCSV": "Scarica come CSV.", + "downloadExcel": "Scarica come XLSX.", "uploadCSV": "Carica CSV.", "import": "Importa", "importMetadata": "Importa metadati", diff --git a/packages/nc-gui/lang/iw.json b/packages/nc-gui/lang/iw.json index 45e373e57d..0ae3bdd7db 100644 --- a/packages/nc-gui/lang/iw.json +++ b/packages/nc-gui/lang/iw.json @@ -309,6 +309,7 @@ "importExcel": "ייבוא ​​Excel", "importCSV": "Import CSV", "downloadCSV": "הורד כמו CSV.", + "downloadExcel": "הורד כמו XLSX.", "uploadCSV": "העלה CSV.", "import": "יְבוּא", "importMetadata": "ייבוא ​​מטא נתונים", diff --git a/packages/nc-gui/lang/ja.json b/packages/nc-gui/lang/ja.json index 40e5792eb1..45a356abf8 100644 --- a/packages/nc-gui/lang/ja.json +++ b/packages/nc-gui/lang/ja.json @@ -309,6 +309,7 @@ "importExcel": "エクセルファイルをインポート", "importCSV": "Import CSV", "downloadCSV": "CSVをダウンロード", + "downloadExcel": "XLSXをダウンロード", "uploadCSV": "CSVをアップロード", "import": "インポート", "importMetadata": "メタデータをインポート", diff --git a/packages/nc-gui/lang/ko.json b/packages/nc-gui/lang/ko.json index a0f0259d99..af75a190bd 100644 --- a/packages/nc-gui/lang/ko.json +++ b/packages/nc-gui/lang/ko.json @@ -309,6 +309,7 @@ "importExcel": "엑셀 가져오기", "importCSV": "CSV 가져오기", "downloadCSV": "CSV 다운로드", + "downloadExcel": "XLSX 다운로드", "uploadCSV": "CSV 업로드", "import": "가져오기", "importMetadata": "메타 데이터 가져오기", diff --git a/packages/nc-gui/lang/lv.json b/packages/nc-gui/lang/lv.json index f2163b25e8..81d12e1e31 100644 --- a/packages/nc-gui/lang/lv.json +++ b/packages/nc-gui/lang/lv.json @@ -309,6 +309,7 @@ "importExcel": "Importēt Excel", "importCSV": "Import CSV", "downloadCSV": "Lejupielādēt kā CSV", + "downloadExcel": "Lejupielādēt kā XLSX", "uploadCSV": "Augšupielādēt CSV", "import": "Importēt", "importMetadata": "Importēt metadatus", diff --git a/packages/nc-gui/lang/nl.json b/packages/nc-gui/lang/nl.json index 1e643d36b8..14eca36408 100644 --- a/packages/nc-gui/lang/nl.json +++ b/packages/nc-gui/lang/nl.json @@ -309,6 +309,7 @@ "importExcel": "Excel importeren", "importCSV": "Import CSV", "downloadCSV": "Download als CSV", + "downloadExcel": "Download als XLSX", "uploadCSV": "Upload CSV", "import": "Importeren", "importMetadata": "Importeer Metadata", diff --git a/packages/nc-gui/lang/no.json b/packages/nc-gui/lang/no.json index 3714b6dddf..1a9ef09ec3 100644 --- a/packages/nc-gui/lang/no.json +++ b/packages/nc-gui/lang/no.json @@ -309,6 +309,7 @@ "importExcel": "Importer Excel.", "importCSV": "Import CSV", "downloadCSV": "Last ned som CSV.", + "downloadExcel": "Last ned som XLSX.", "uploadCSV": "Last opp CSV.", "import": "Importer", "importMetadata": "Importer metadata", diff --git a/packages/nc-gui/lang/pl.json b/packages/nc-gui/lang/pl.json index 051d85875e..0aaaa91a56 100644 --- a/packages/nc-gui/lang/pl.json +++ b/packages/nc-gui/lang/pl.json @@ -309,6 +309,7 @@ "importExcel": "Importuj Excel.", "importCSV": "Import CSV", "downloadCSV": "Pobierz jako CSV.", + "downloadExcel": "Pobierz jako XLSX.", "uploadCSV": "Prześlij CSV.", "import": "Import", "importMetadata": "Importuj metadane", diff --git a/packages/nc-gui/lang/pt.json b/packages/nc-gui/lang/pt.json index 8961d7bdd6..5e41c2b877 100644 --- a/packages/nc-gui/lang/pt.json +++ b/packages/nc-gui/lang/pt.json @@ -309,6 +309,7 @@ "importExcel": "Importar Excel.", "importCSV": "Import CSV", "downloadCSV": "Baixe como CSV.", + "downloadExcel": "Baixe como XLSX.", "uploadCSV": "Carregar CSV.", "import": "Importar", "importMetadata": "Importar Metadados", diff --git a/packages/nc-gui/lang/pt_BR.json b/packages/nc-gui/lang/pt_BR.json index cb24beebc0..20201e13b5 100644 --- a/packages/nc-gui/lang/pt_BR.json +++ b/packages/nc-gui/lang/pt_BR.json @@ -309,6 +309,7 @@ "importExcel": "Importar Excel.", "importCSV": "Import CSV", "downloadCSV": "Baixe como CSV.", + "downloadExcel": "Baixe como XLSX.", "uploadCSV": "Carregar CSV.", "import": "Importar", "importMetadata": "Importar Metadados", diff --git a/packages/nc-gui/lang/ru.json b/packages/nc-gui/lang/ru.json index 5ab0ef2225..c3e3934d8c 100644 --- a/packages/nc-gui/lang/ru.json +++ b/packages/nc-gui/lang/ru.json @@ -308,6 +308,7 @@ "importExcel": "Импорт из Excel", "importCSV": "Import CSV", "downloadCSV": "Скачать как CSV.", + "downloadExcel": "Скачать как XLSX.", "uploadCSV": "Загрузить CSV.", "import": "Импортировать", "importMetadata": "Импорт метаданных", diff --git a/packages/nc-gui/lang/sl.json b/packages/nc-gui/lang/sl.json index 9f56795611..28968cea06 100644 --- a/packages/nc-gui/lang/sl.json +++ b/packages/nc-gui/lang/sl.json @@ -309,6 +309,7 @@ "importExcel": "Uvoz Excel.", "importCSV": "Import CSV", "downloadCSV": "Prenesite kot CSV.", + "downloadExcel": "Prenesite kot XLSX.", "uploadCSV": "Upload CSV.", "import": "Uvozi", "importMetadata": "Uvozi metapodatkov", diff --git a/packages/nc-gui/lang/sv.json b/packages/nc-gui/lang/sv.json index de5eede18e..914b8e432d 100644 --- a/packages/nc-gui/lang/sv.json +++ b/packages/nc-gui/lang/sv.json @@ -309,6 +309,7 @@ "importExcel": "Import excel", "importCSV": "Import CSV", "downloadCSV": "Hämta som CSV", + "downloadExcel": "Hämta som XLSX", "uploadCSV": "Ladda upp CSV", "import": "Importera", "importMetadata": "Importera metadata", diff --git a/packages/nc-gui/lang/th.json b/packages/nc-gui/lang/th.json index 0a8cf296bb..6459bb5d76 100644 --- a/packages/nc-gui/lang/th.json +++ b/packages/nc-gui/lang/th.json @@ -309,6 +309,7 @@ "importExcel": "นำเข้า Excel", "importCSV": "Import CSV", "downloadCSV": "ดาวน์โหลดเป็น CSV", + "downloadExcel": "ดาวน์โหลดเป็น XLSX", "uploadCSV": "อัปโหลด CSV", "import": "นำเข้า", "importMetadata": "เมทาดานำเข้า", diff --git a/packages/nc-gui/lang/tr.json b/packages/nc-gui/lang/tr.json index 79bbbe78d9..37938862e0 100644 --- a/packages/nc-gui/lang/tr.json +++ b/packages/nc-gui/lang/tr.json @@ -309,6 +309,7 @@ "importExcel": "Excel'i içe aktar", "importCSV": "Import CSV", "downloadCSV": "CSV olarak indir", + "downloadExcel": "XLSX olarak indir", "uploadCSV": "CSV yükle", "import": "İçe aktar", "importMetadata": "Meta verilerini içe aktar", diff --git a/packages/nc-gui/lang/uk.json b/packages/nc-gui/lang/uk.json index 1446b367d0..dfb4ec2d17 100644 --- a/packages/nc-gui/lang/uk.json +++ b/packages/nc-gui/lang/uk.json @@ -309,6 +309,7 @@ "importExcel": "Імпорт Excel", "importCSV": "Import CSV", "downloadCSV": "Завантажити як CSV", + "downloadExcel": "Завантажити як XLSX", "uploadCSV": "Завантажити CSV", "import": "Імпортувати", "importMetadata": "Імпорт метаданих", diff --git a/packages/nc-gui/lang/vi.json b/packages/nc-gui/lang/vi.json index cfe69a98b9..04fcc91e2e 100644 --- a/packages/nc-gui/lang/vi.json +++ b/packages/nc-gui/lang/vi.json @@ -309,6 +309,7 @@ "importExcel": "Nhập Excel.", "importCSV": "Import CSV", "downloadCSV": "Tải về dưới dạng CSV.", + "downloadExcel": "Tải về dưới dạng XLSX.", "uploadCSV": "Tải lên CSV.", "import": "Nhập khẩu", "importMetadata": "Nhập siêu dữ liệu", diff --git a/packages/nc-gui/lang/zh_CN.json b/packages/nc-gui/lang/zh_CN.json index 38990ec059..cfb15971f4 100644 --- a/packages/nc-gui/lang/zh_CN.json +++ b/packages/nc-gui/lang/zh_CN.json @@ -309,6 +309,7 @@ "importExcel": "导入Excel", "importCSV": "Import CSV", "downloadCSV": "下载为CSV", + "downloadExcel": "下载为XLSX", "uploadCSV": "上传CSV", "import": "导入", "importMetadata": "导入元数据", diff --git a/packages/nc-gui/lang/zh_HK.json b/packages/nc-gui/lang/zh_HK.json index 614c1babeb..aafd0be421 100644 --- a/packages/nc-gui/lang/zh_HK.json +++ b/packages/nc-gui/lang/zh_HK.json @@ -309,6 +309,7 @@ "importExcel": "導入Excel.", "importCSV": "Import CSV", "downloadCSV": "下載為CSV.", + "downloadExcel": "下載為XLSX.", "uploadCSV": "上傳CSV.", "import": "導出去file", "importMetadata": "import metadata", diff --git a/packages/nc-gui/lang/zh_TW.json b/packages/nc-gui/lang/zh_TW.json index 9003a2d005..a1ffb8f999 100644 --- a/packages/nc-gui/lang/zh_TW.json +++ b/packages/nc-gui/lang/zh_TW.json @@ -309,6 +309,7 @@ "importExcel": "匯入 Excel", "importCSV": "匯入 CSV", "downloadCSV": "下載為 CSV", + "downloadExcel": "下載為 XLSX", "uploadCSV": "上傳 CSV", "import": "匯入", "importMetadata": "匯入中繼資料", @@ -517,4 +518,4 @@ "futureRelease": "即將推出!" } } -} \ No newline at end of file +} From ec51481b9e284669cc09bf9968e31cb3a1348983 Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Wed, 27 Jul 2022 12:34:17 +0200 Subject: [PATCH 5/8] feat: factorise get export data logic on moreview vue --- .../spreadsheet/components/MoreActions.vue | 124 +++++++----------- 1 file changed, 44 insertions(+), 80 deletions(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue index 6e6ec7df43..5e44095ad1 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue @@ -235,47 +235,7 @@ export default { let c = 1; try { while (!isNaN(offset) && offset > -1) { - let res; - if (this.publicViewId) { - console.log('IF', this.publicViewId) - res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.EXCEL, { - responseType: 'blob', - query: { - fields: - this.queryParams && - this.queryParams.fieldsOrder && - this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]), - offset, - sortArrJson: JSON.stringify( - this.reqPayload && - this.reqPayload.sorts && - this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({ - direction, - fk_column_id, - })) - ), - filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters), - }, - headers: { - 'xc-password': this.reqPayload && this.reqPayload.password, - }, - }); - } else { - console.log('ELSE') - res = await this.$api.dbViewRow.export( - 'noco', - this.projectName, - this.meta.title, - this.selectedView.title, - ExportTypes.EXCEL, - { - responseType: 'base64', - query: { - offset, - }, - } - ); - } + const res = await this.getExportData({ offset, exportType: ExportTypes.EXCEL, responseType: 'base64' }); const workbook = XLSX.read(res.data, { type: 'base64' }); XLSX.writeFile(workbook, `${this.meta.title}_exported_${c++}.xlsx`); @@ -297,45 +257,7 @@ export default { try { while (!isNaN(offset) && offset > -1) { - let res; - if (this.publicViewId) { - res = await this.$api.public.csvExport(this.publicViewId, ExportTypes.CSV, { - responseType: 'blob', - query: { - fields: - this.queryParams && - this.queryParams.fieldsOrder && - this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]), - offset, - sortArrJson: JSON.stringify( - this.reqPayload && - this.reqPayload.sorts && - this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({ - direction, - fk_column_id, - })) - ), - filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters), - }, - headers: { - 'xc-password': this.reqPayload && this.reqPayload.password, - }, - }); - } else { - res = await this.$api.dbViewRow.export( - 'noco', - this.projectName, - this.meta.title, - this.selectedView.title, - ExportTypes.CSV, - { - responseType: 'blob', - query: { - offset, - }, - } - ); - } + const res = await this.getExportData({ offset, exportType: ExportTypes.CSV, responseType: 'blob' }) const { data } = res; offset = +res.headers['nc-export-offset']; @@ -352,6 +274,48 @@ export default { this.$toast.error(e.message).goAway(3000); } }, + async getExportData({ offset, exportType, responseType }) { + let res; + if (this.publicViewId) { + res = await this.$api.public.csvExport(this.publicViewId, exportType, { + responseType, + query: { + fields: + this.queryParams && + this.queryParams.fieldsOrder && + this.queryParams.fieldsOrder.filter(c => this.queryParams.showFields[c]), + offset, + sortArrJson: JSON.stringify( + this.reqPayload && + this.reqPayload.sorts && + this.reqPayload.sorts.map(({ fk_column_id, direction }) => ({ + direction, + fk_column_id, + })) + ), + filterArrJson: JSON.stringify(this.reqPayload && this.reqPayload.filters), + }, + headers: { + 'xc-password': this.reqPayload && this.reqPayload.password, + }, + }); + } else { + res = await this.$api.dbViewRow.export( + 'noco', + this.projectName, + this.meta.title, + this.selectedView.title, + exportType, + { + responseType, + query: { + offset, + }, + } + ); + } + return res; + }, async importData(columnMappings) { try { const data = this.parsedCsv.data; From 1b3a3629f561680e74a5be5dd6bbf8a23bdaae9b Mon Sep 17 00:00:00 2001 From: Louis Delbosc Date: Tue, 2 Aug 2022 10:13:53 +0200 Subject: [PATCH 6/8] fix: update comment --- .../components/project/spreadsheet/components/MoreActions.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue index 5e44095ad1..65c41dda58 100644 --- a/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue +++ b/packages/nc-gui/components/project/spreadsheet/components/MoreActions.vue @@ -32,7 +32,7 @@ mdi-download-outline - + {{ $t('activity.downloadExcel') }} From d63fb1b41d17001f09cdb9f529b07b3378791808 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Wed, 3 Aug 2022 21:31:12 +0530 Subject: [PATCH 7/8] fix: cypress corrections for new menu Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- scripts/cypress/support/page_objects/mainPage.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/cypress/support/page_objects/mainPage.js b/scripts/cypress/support/page_objects/mainPage.js index b8d321561b..b388264871 100644 --- a/scripts/cypress/support/page_objects/mainPage.js +++ b/scripts/cypress/support/page_objects/mainPage.js @@ -251,22 +251,27 @@ export class _mainPage { shareViewList = () => { cy.get(".nc-actions-menu-btn").click(); - return cy.getActiveMenu().find('[role="menuitem"]').eq(2); + return cy.getActiveMenu().find('[role="menuitem"]').contains("Shared View List"); }; downloadCsv = () => { cy.get(".nc-actions-menu-btn").click(); - return cy.getActiveMenu().find('[role="menuitem"]').eq(0); + return cy.getActiveMenu().find('[role="menuitem"]').contains("Download as CSV"); + }; + + downloadExcel = () => { + cy.get(".nc-actions-menu-btn").click(); + return cy.getActiveMenu().find('[role="menuitem"]').contains("Download as XLSX"); }; uploadCsv = () => { cy.get(".nc-actions-menu-btn").click(); - return cy.getActiveMenu().find('[role="menuitem"]').eq(1); + return cy.getActiveMenu().find('[role="menuitem"]').contains("Upload CSV"); }; automations = () => { cy.get(".nc-actions-menu-btn").click(); - return cy.getActiveMenu().find('[role="menuitem"]').eq(3); + return cy.getActiveMenu().find('[role="menuitem"]').contains("Webhooks"); }; hideField = (field) => { From 18a150e39ee10b08034dc25bb372db17700be4d6 Mon Sep 17 00:00:00 2001 From: Raju Udava <86527202+dstala@users.noreply.github.com> Date: Wed, 3 Aug 2022 22:41:53 +0530 Subject: [PATCH 8/8] fix: cy-menu-cnt-corrections for different role Signed-off-by: Raju Udava <86527202+dstala@users.noreply.github.com> --- scripts/cypress/integration/spec/roleValidation.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/cypress/integration/spec/roleValidation.spec.js b/scripts/cypress/integration/spec/roleValidation.spec.js index f9156c7a9f..a54aacc181 100644 --- a/scripts/cypress/integration/spec/roleValidation.spec.js +++ b/scripts/cypress/integration/spec/roleValidation.spec.js @@ -217,7 +217,7 @@ export function _viewMenu(roleType, previewMode, navDrawListCnt) { // let navDrawListCnt = 2; // Download CSV - let actionsMenuItemsCnt = 1; + let actionsMenuItemsCnt = 2; cy.openTableTab(columnName, 25); @@ -234,10 +234,10 @@ export function _viewMenu(roleType, previewMode, navDrawListCnt) { if (roleType == "owner" || roleType == "creator") { navDrawListCnt = 3; // Download CSV / Upload CSV / Shared View List / Webhook - actionsMenuItemsCnt = 4; + actionsMenuItemsCnt = 5; } else if (roleType == "editor") { // Download CSV / Upload CSV - actionsMenuItemsCnt = 2; + actionsMenuItemsCnt = 3; } cy.get(".v-navigation-drawer__content")