From 0ddded4a869052ebaa6aa2e6f00025af75d29104 Mon Sep 17 00:00:00 2001 From: Maximilian Krambach Date: Thu, 28 Feb 2019 12:46:39 +0100 Subject: [PATCH] csv example export refactor - make csv example data respect config separators - refactor at a more central place --- .../core/ui-services/csv-export.service.ts | 31 +++++++++++- .../agenda-import-list.component.ts | 21 ++++---- .../services/motion-csv-export.service.ts | 20 +++----- .../services/statute-csv-export.service.ts | 24 +++------ .../user-import/user-import-list.component.ts | 49 ++++++++++++++----- 5 files changed, 90 insertions(+), 55 deletions(-) diff --git a/client/src/app/core/ui-services/csv-export.service.ts b/client/src/app/core/ui-services/csv-export.service.ts index ca7329ef9..d58ed6d5f 100644 --- a/client/src/app/core/ui-services/csv-export.service.ts +++ b/client/src/app/core/ui-services/csv-export.service.ts @@ -75,7 +75,7 @@ export class CsvExportService { * @param filename name of the resulting file * @param options optional: * lineSeparator (defaults to \r\n windows style line separator), - * columnseparator defaults to semicolon (other usual separators are ',' '\T' (tab), ' 'whitespace) + * columnseparator defaults to configured option (',' , other values are ';', '\t' (tab), ' 'whitespace) */ public export( models: T[], @@ -190,4 +190,33 @@ export class CsvExportService { const temp = input.charAt(0).toUpperCase() + input.slice(1); return this.translate.instant(temp); } + + public dummyCSVExport(header: string[], rows: (string | number | boolean | null)[][], filename: string): void { + const separator = this.config.instant('general_csv_separator'); + const tsList = this.checkCsvTextSafety(separator, ['"', "'", '`', '/', '\\', ';', '.']); + const headerRow = [header.map(item => this.translate.instant(item)).join(separator)]; + const content = rows.map(row => + row + .map(item => { + if (item === null) { + return ''; + } + if (typeof item === 'string') { + return `${tsList[0]}${item}${tsList[0]}`; + } else if (typeof item === 'boolean') { + return item ? '1' : '0'; + } else { + return `${item}`; + } + }) + .join(separator) + ); + const csvContentAsString = headerRow.concat(content).join('\r\n'); + const encoding = this.config.instant<'utf-8' | 'iso-8859-15'>('general_csv_encoding'); + if (encoding === 'iso-8859-15') { + this.exporter.saveFile(this.exporter.convertTo8859_15(csvContentAsString), filename, 'text/csv'); + } else { + this.exporter.saveFile(csvContentAsString, filename, 'text/csv'); + } + } } diff --git a/client/src/app/site/agenda/components/agenda-import-list/agenda-import-list.component.ts b/client/src/app/site/agenda/components/agenda-import-list/agenda-import-list.component.ts index a7a74d1ae..23c218677 100644 --- a/client/src/app/site/agenda/components/agenda-import-list/agenda-import-list.component.ts +++ b/client/src/app/site/agenda/components/agenda-import-list/agenda-import-list.component.ts @@ -6,8 +6,8 @@ import { TranslateService } from '@ngx-translate/core'; import { AgendaImportService } from '../../agenda-import.service'; import { BaseImportListComponent } from 'app/site/base/base-import-list'; +import { CsvExportService } from 'app/core/ui-services/csv-export.service'; import { DurationService } from 'app/core/ui-services/duration.service'; -import { FileExportService } from 'app/core/ui-services/file-export.service'; import { itemVisibilityChoices } from 'app/shared/models/agenda/item'; import { ViewCreateTopic } from '../../models/view-create-topic'; @@ -41,7 +41,7 @@ export class AgendaImportListComponent extends BaseImportListComponent this.translate.instant(item)) - .join(','); + const headerRow = ['Title', 'Text', 'Duration', 'Comment', 'Internal item']; const rows = [ - headerRow, - 'Demo 1,Demo text 1,1:00,test comment,', - 'Break,,0:10,,internal', - 'Demo 2,Demo text 2,1:30,,hidden' + ['Demo 1', 'Demo text 1', '1:00', 'test comment', null], + ['Break', null, '0:10', null, 'internal', null], + ['Demo 2', 'Demo text 2', '1:30', null, 'hidden'] ]; - this.exporter.saveFile(rows.join('\n'), this.translate.instant('Topic example') + '.csv', 'text/csv'); + this.exporter.dummyCSVExport( + headerRow, + rows, + `${this.translate.instant('Agenda')}-${this.translate.instant('example')}.csv` + ); } /** diff --git a/client/src/app/site/motions/services/motion-csv-export.service.ts b/client/src/app/site/motions/services/motion-csv-export.service.ts index 1c9b706c0..58f0f9848 100644 --- a/client/src/app/site/motions/services/motion-csv-export.service.ts +++ b/client/src/app/site/motions/services/motion-csv-export.service.ts @@ -3,7 +3,6 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CsvExportService, CsvColumnDefinitionProperty } from 'app/core/ui-services/csv-export.service'; -import { FileExportService } from 'app/core/ui-services/file-export.service'; import { InfoToExport } from './motion-pdf.service'; import { ViewMotion } from '../models/view-motion'; @@ -20,11 +19,7 @@ export class MotionCsvExportService { * @param csvExport CsvExportService * @param translate TranslateService */ - public constructor( - private csvExport: CsvExportService, - private translate: TranslateService, - private fileExport: FileExportService - ) {} + public constructor(private csvExport: CsvExportService, private translate: TranslateService) {} /** * Export all motions as CSV @@ -67,15 +62,12 @@ export class MotionCsvExportService { } public exportDummyMotion(): void { - const headerRow = ['Identifier', 'Title', 'Text', 'Reason', 'Submitters', 'Category', 'Origin', 'Motion block'] - .map(item => this.translate.instant(item)) - .join(','); + const headerRow = ['Identifier', 'Title', 'Text', 'Reason', 'Submitters', 'Category', 'Origin', 'Motion block']; const rows = [ - headerRow, - 'A1,Title 1,Text 1,Reason 1,Submitter A,Category A,"Last Year Conference A", Block A', - 'B1,Title 2,Text 2,Reason 2,Submitter B, Category B,, Block A', - ',Title 3, Text 3,,,,,' + ['A1', 'Title 1', 'Text 1', 'Reason 1', 'Submitter A', 'Category A', 'Last Year Conference A', 'Block A'], + ['B1', 'Title 2', 'Text 2', 'Reason 2', 'Submitter B', 'Category B', null, 'Block A'], + [null, 'Title 3', 'Text 3', null, null, null, null, null] ]; - this.fileExport.saveFile(rows.join('\n'), this.translate.instant('motions-example') + '.csv', 'text/csv'); + this.csvExport.dummyCSVExport(headerRow, rows, `${this.translate.instant('motions-example')}.csv`); } } diff --git a/client/src/app/site/motions/services/statute-csv-export.service.ts b/client/src/app/site/motions/services/statute-csv-export.service.ts index 87f68e193..20346622e 100644 --- a/client/src/app/site/motions/services/statute-csv-export.service.ts +++ b/client/src/app/site/motions/services/statute-csv-export.service.ts @@ -4,7 +4,6 @@ import { TranslateService } from '@ngx-translate/core'; import { CsvExportService, CsvColumnDefinitionProperty } from 'app/core/ui-services/csv-export.service'; import { ViewStatuteParagraph } from '../models/view-statute-paragraph'; -import { FileExportService } from 'app/core/ui-services/file-export.service'; /** * Exports CSVs for statute paragraphs. @@ -18,13 +17,8 @@ export class StatuteCsvExportService { * * @param csvExport CsvExportService * @param translate TranslateService - * @param fileExport FileExportService */ - public constructor( - private csvExport: CsvExportService, - private translate: TranslateService, - private fileExport: FileExportService - ) {} + public constructor(private csvExport: CsvExportService, private translate: TranslateService) {} /** * Export all statute paragraphs as CSV @@ -43,17 +37,13 @@ export class StatuteCsvExportService { * Exports a short example file */ public exportDummyCSV(): void { - const headerRow = ['Title', 'Text'].map(item => this.translate.instant(item)).join(','); + const header = ['Title', 'Text']; const rows = [ - headerRow, - '§1,"This is the first section"', - '"§1, A 3", "This is another important aspect"', - '§2,Yet another' + ['§1', 'This is the first section'], + ['§1, A 3', 'This is another important aspect'], + ['§2', 'Yet another'] ]; - this.fileExport.saveFile( - rows.join('\n'), - `${this.translate.instant('Statute')}-${this.translate.instant('example')}.csv`, - 'text/csv' - ); + const filename = `${this.translate.instant('Statute')}-${this.translate.instant('example')}.csv`; + this.csvExport.dummyCSVExport(header, rows, filename); } } diff --git a/client/src/app/site/users/components/user-import/user-import-list.component.ts b/client/src/app/site/users/components/user-import/user-import-list.component.ts index d59ade6c3..d5a94274d 100644 --- a/client/src/app/site/users/components/user-import/user-import-list.component.ts +++ b/client/src/app/site/users/components/user-import/user-import-list.component.ts @@ -4,7 +4,7 @@ import { Title } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; import { BaseImportListComponent } from 'app/site/base/base-import-list'; -import { FileExportService } from 'app/core/ui-services/file-export.service'; +import { CsvExportService } from 'app/core/ui-services/csv-export.service'; import { FormBuilder, FormGroup } from '@angular/forms'; import { NewEntry } from 'app/core/ui-services/base-import.service'; import { UserImportService } from '../../services/user-import.service'; @@ -27,7 +27,7 @@ export class UserImportListComponent extends BaseImportListComponent { * @param matSnackBar snackbar for displaying errors * @param formBuilder: FormBuilder for the textArea * @param translate the translate service - * @param exporter: csv export service for dummy dat + * @param exporter: csv export service for dummy data * @param importer: The motion csv import service */ public constructor( @@ -35,7 +35,7 @@ export class UserImportListComponent extends BaseImportListComponent { matSnackBar: MatSnackBar, formBuilder: FormBuilder, public translate: TranslateService, - private exporter: FileExportService, + private exporter: CsvExportService, importer: UserImportService ) { super(importer, titleService, translate, matSnackBar); @@ -59,17 +59,40 @@ export class UserImportListComponent extends BaseImportListComponent { 'Is a committee', 'Initial password', 'Email' - ] - .map(item => this.translate.instant(item)) - .join(','); - const rows = [ - headerRow, - 'Dr.,Max,Mustermann,"Berlin",1234567890,"Delegates, Staff",xyz,1,1,,initialPassword,', - ',John,Doe,Washington,75/99/8-2,Committees,"This is a comment, without doubt",1,1,,,john.doe@email.com', - ',Fred,Bloggs,London,,,,,,,,', - ',,Executive Board,,,,,,,1,,' ]; - this.exporter.saveFile(rows.join('\n'), this.translate.instant('participants-example') + '.csv', 'text/csv'); + const rows = [ + [ + 'Dr.', + 'Max', + 'Mustermann', + 'Berlin', + 1234567890, + 'Delegates, Staff', + 'xyz', + 1, + 1, + , + 'initialPassword', + null + ], + [ + null, + 'John', + 'Doe', + 'Washington', + '75/99/8-2', + 'Committees', + 'This is a comment, without doubt', + 1, + 1, + null, + null, + 'john.doe@email.com' + ], + [null, 'Fred', 'Bloggs', 'London', null, null, null, null, null, null, null, null], + [null, null, 'Executive Board', null, null, null, null, null, null, 1, null, null] + ]; + this.exporter.dummyCSVExport(headerRow, rows, `${this.translate.instant('participants-example')}.csv`); } /**