diff --git a/libs/portal-integration-angular/src/lib/services/export-data-service.spec.ts b/libs/portal-integration-angular/src/lib/services/export-data-service.spec.ts index bf3aedd3..d88e0162 100644 --- a/libs/portal-integration-angular/src/lib/services/export-data-service.spec.ts +++ b/libs/portal-integration-angular/src/lib/services/export-data-service.spec.ts @@ -247,39 +247,39 @@ describe('ExportDataService', () => { const expectedCsv = 'Name,Description,Start date,End date,Status,Responsible,Modification date,Creation user,Test number' + '\r\nsome name,,' + - dateUtils.localizedDate('2023-09-13T09:34:05Z') + + '"' + dateUtils.localizedDate('2023-09-13T09:34:05Z') + '"' + ',' + - dateUtils.localizedDate('2023-09-14T09:34:09Z') + + '"' + dateUtils.localizedDate('2023-09-14T09:34:09Z') + '"' + ',some status,someone responsible,' + - dateUtils.localizedDate('2023-09-12T09:34:11.997048Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:34:11.997048Z') + '"' + ',creation user,1' + '\r\nexample,example description,' + - dateUtils.localizedDate('2023-09-12T09:33:53Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:33:53Z') + '"' + ',' + - dateUtils.localizedDate('2023-09-13T09:33:55Z') + + '"' + dateUtils.localizedDate('2023-09-13T09:33:55Z') + '"' + ',some status example,,' + - dateUtils.localizedDate('2023-09-12T09:33:58.544494Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:33:58.544494Z') + '"' + ',,3.141' + '\r\nname 1,,' + - dateUtils.localizedDate('2023-09-14T09:34:22Z') + + '"' + dateUtils.localizedDate('2023-09-14T09:34:22Z') + '"' + ',' + - dateUtils.localizedDate('2023-09-15T09:34:24Z') + + '"' + dateUtils.localizedDate('2023-09-15T09:34:24Z') + '"' + ',status name 1,,' + - dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + '"' + ',,123456789' + '\r\nname 2,,' + - dateUtils.localizedDate('2023-09-14T09:34:22Z') + + '"' + dateUtils.localizedDate('2023-09-14T09:34:22Z') + '"' + ',' + - dateUtils.localizedDate('2023-09-15T09:34:24Z') + + '"' + dateUtils.localizedDate('2023-09-15T09:34:24Z') + '"' + ',status name 2,,' + - dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + '"' + ',,12345.6789' + '\r\nname 3,,' + - dateUtils.localizedDate('2023-09-14T09:34:22Z') + + '"' + dateUtils.localizedDate('2023-09-14T09:34:22Z') + '"' + ',' + - dateUtils.localizedDate('2023-09-15T09:34:24Z') + + '"' + dateUtils.localizedDate('2023-09-15T09:34:24Z') + '"' + ',status name 3,,' + - dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + + '"' + dateUtils.localizedDate('2023-09-12T09:34:27.184086Z') + '"' + ',,7.1' const expectedFilename = 'some-test.csv' const mock = new ElementMock() diff --git a/libs/portal-integration-angular/src/lib/services/export-data.service.ts b/libs/portal-integration-angular/src/lib/services/export-data.service.ts index 02688404..43b4433e 100644 --- a/libs/portal-integration-angular/src/lib/services/export-data.service.ts +++ b/libs/portal-integration-angular/src/lib/services/export-data.service.ts @@ -28,9 +28,10 @@ export class ExportDataService { const dataToExport = this.formatData(columns, translatedData) const delimiter = this.locale.startsWith('de') ? ';' : ',' const dataString = dataToExport - .map((d) => columns.reduce((arr: unknown[], c) => [...arr, d[c.id]], []).join(delimiter)) + .map((d) => columns.reduce((arr: unknown[], c) => [...arr, d[c.id]], []).map((d) => this.escapeDelimiter(delimiter, d)).join(delimiter)) .join('\r\n') - const headerString = (await firstValueFrom(this.translateColumnNames(columns))).map((c) => c.name).join(delimiter) + const headerString = (await firstValueFrom(this.translateColumnNames(columns))).map((c) => c.name).map((c) => this.escapeDelimiter(delimiter, c)).join(delimiter) + const csvString = headerString + '\r\n' + dataString const blob = new Blob(['\ufeff' + csvString], { @@ -56,7 +57,7 @@ export class ExportDataService { columns: { id: string; nameKey: string; columnType: ColumnType }[], data: Record[] ): { [columnId: string]: unknown }[] { - return data.map((d) => + return data.map((d) => columns.reduce((obj, c) => { if (c.columnType === ColumnType.DATE || c.columnType === ColumnType.RELATIVE_DATE) { return { @@ -95,4 +96,21 @@ export class ExportDataService { } return of(data) } -} + + private escapeDelimiter(delimiter: ';' | ',', data: unknown) { + if(data === null || data === undefined) { + return data + } + + let str = String(data) + + if(str.includes('"')) { + str = str.replaceAll('"', '""') + } + + if(str.includes(delimiter)) { + str = `"${str}"` + } + return str + } +} \ No newline at end of file