Merge pull request #4822 from tsiegleauq/motion-export-dialog-addons

Add more option to motion export
This commit is contained in:
Emanuel Schütze 2019-07-05 12:22:42 +02:00 committed by GitHub
commit 3cd9c5497c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 225 additions and 173 deletions

View File

@ -7,6 +7,7 @@ import { TranslateService } from '@ngx-translate/core';
import { ConfigService } from './config.service';
import { HttpService } from '../core-services/http.service';
import { ExportFormData } from 'app/site/motions/modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
/**
* Enumeration to define possible values for the styling.
@ -177,6 +178,7 @@ export class PdfDocumentService {
private async getStandardPaper(
documentContent: object,
metadata?: object,
exportInfo?: ExportFormData,
imageUrls?: string[],
customMargins?: [number, number, number, number],
landscape?: boolean
@ -197,12 +199,14 @@ export class PdfDocumentService {
fontSize: this.configService.instant('general_export_pdf_fontsize')
},
header: this.getHeader(customMargins ? [customMargins[0], customMargins[2]] : null),
// TODO: option for no footer, wherever this can be defined
footer: (currentPage, pageCount) => {
return this.getFooter(
currentPage,
pageCount,
customMargins ? [customMargins[0], customMargins[2]] : null
customMargins ? [customMargins[0], customMargins[2]] : null,
exportInfo
);
},
info: metadata,
@ -337,14 +341,35 @@ export class PdfDocumentService {
* @param lrMargin optionally overriding the margins
* @returns the footer doc definition
*/
private getFooter(currentPage: number, pageCount: number, lrMargin?: [number, number]): object {
private getFooter(
currentPage: number,
pageCount: number,
lrMargin?: [number, number],
exportInfo?: ExportFormData
): object {
const columns = [];
const showPage = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('page') : true;
const showDate = exportInfo && exportInfo.pdfOptions ? exportInfo.pdfOptions.includes('date') : false;
let logoContainerWidth: string;
let pageNumberPosition: string;
let logoContainerSize: number[];
const logoFooterLeftUrl = this.configService.instant<any>('logo_pdf_footer_L').path;
const logoFooterRightUrl = this.configService.instant<any>('logo_pdf_footer_R').path;
let footerText = '';
if (showPage) {
footerText += `${currentPage} / ${pageCount}`;
if (showDate) {
footerText += '\n';
}
}
if (showDate) {
footerText += `(${this.translate.instant('Created on')}: ${new Date().toLocaleDateString(
this.translate.currentLang
)})`;
}
// if there is a single logo, give it a lot of space
if (logoFooterLeftUrl && logoFooterRightUrl) {
logoContainerWidth = '20%';
@ -377,7 +402,7 @@ export class PdfDocumentService {
// add the page number
columns.push({
text: `${currentPage} / ${pageCount}`,
text: footerText,
style: 'footerPageNumber',
alignment: pageNumberPosition
});
@ -407,8 +432,8 @@ export class PdfDocumentService {
* @param filename the name of the file to use
* @param metadata
*/
public download(docDefinition: object, filename: string, metadata?: object): void {
this.getStandardPaper(docDefinition, metadata).then(doc => {
public download(docDefinition: object, filename: string, metadata?: object, exportInfo?: ExportFormData): void {
this.getStandardPaper(docDefinition, metadata, exportInfo).then(doc => {
this.createPdf(doc, filename);
});
}
@ -421,7 +446,7 @@ export class PdfDocumentService {
* @param metadata
*/
public downloadLandscape(docDefinition: object, filename: string, metadata?: object): void {
this.getStandardPaper(docDefinition, metadata, null, [50, 80, 50, 75], true).then(doc => {
this.getStandardPaper(docDefinition, metadata, null, null, [50, 80, 50, 75], true).then(doc => {
this.createPdf(doc, filename);
});
}

View File

@ -1389,7 +1389,11 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
* Click handler for the pdf button
*/
public onDownloadPdf(): void {
this.pdfExport.exportSingleMotion(this.motion, this.lnMode, this.crMode);
this.pdfExport.exportSingleMotion(this.motion, {
lnMode: this.lnMode,
crMode: this.crMode,
comments: this.motion.commentSectionIds
});
}
/**

View File

@ -6,9 +6,9 @@
<div>
<p class="toggle-group-head" translate>Format</p>
<mat-button-toggle-group class="smaller-buttons" formControlName="format">
<mat-button-toggle value="pdf">PDF</mat-button-toggle>
<mat-button-toggle value="csv">CSV</mat-button-toggle>
<mat-button-toggle value="xlsx">XLSX</mat-button-toggle>
<mat-button-toggle [value]="fileFormat.PDF">PDF</mat-button-toggle>
<mat-button-toggle [value]="fileFormat.CSV">CSV</mat-button-toggle>
<mat-button-toggle [value]="fileFormat.XLSX">XLSX</mat-button-toggle>
</mat-button-toggle-group>
</div>
@ -54,7 +54,16 @@
</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div *ngIf="commentsToExport.length && exportForm.get('format').value === 'pdf'">
<div>
<p class="toggle-group-head" translate>PDF options</p>
<mat-button-toggle-group class="smaller-buttons" multiple formControlName="pdfOptions">
<mat-button-toggle value="toc"> <span translate>Table of Contents</span> </mat-button-toggle>
<mat-button-toggle value="page"> <span translate>Page number</span> </mat-button-toggle>
<mat-button-toggle value="date"> <span translate>Current date</span> </mat-button-toggle>
</mat-button-toggle-group>
</div>
<div *ngIf="commentsToExport.length">
<p class="toggle-group-head" translate>Comments</p>
<mat-button-toggle-group class="smaller-buttons" multiple formControlName="comments">
<mat-button-toggle *ngFor="let comment of commentsToExport" [value]="comment.id">

View File

@ -9,6 +9,30 @@ import { LineNumberingMode, ChangeRecoMode } from 'app/site/motions/models/view-
import { InfoToExport } from 'app/site/motions/services/motion-pdf.service';
import { ViewMotionCommentSection } from 'app/site/motions/models/view-motion-comment-section';
import { motionImportExportHeaderOrder, noMetaData } from 'app/site/motions/motion-import-export-order';
import { StorageService } from 'app/core/core-services/storage.service';
import { auditTime } from 'rxjs/operators';
/**
* Determine the possible file format
*/
export enum FileFormat {
PDF = 1,
CSV,
XLSX
}
/**
* Shape the structure of the dialog data
*/
export interface ExportFormData {
format?: FileFormat;
lnMode?: LineNumberingMode;
crMode?: ChangeRecoMode;
content?: string[];
metaInfo?: InfoToExport[];
pdfOptions?: string[];
comments?: number[];
}
/**
* Dialog component to determine exporting.
@ -29,56 +53,37 @@ export class MotionExportDialogComponent implements OnInit {
*/
public crMode = ChangeRecoMode;
/**
* to use the format in the template
*/
public fileFormat = FileFormat;
/**
* The form that contains the export information.
*/
public exportForm: FormGroup;
/**
* determine the default format to export
* The default export values in contrast to the restored values
*/
private defaultExportFormat = 'pdf';
/**
* Determine the default content to export.
*/
private defaultContentToExport = ['text', 'reason'];
private defaults: ExportFormData = {
format: FileFormat.PDF,
content: ['text', 'reason'],
pdfOptions: ['toc', 'page'],
metaInfo: ['submitters', 'state', 'recommendation', 'category', 'origin', 'tags', 'motion_block', 'polls', 'id']
};
/**
* Determine the export order of the meta data
*/
public metaInfoExportOrder: string[];
/**
* Determine the default meta info to export.
*/
private defaultInfoToExport: InfoToExport[] = [
'submitters',
'state',
'recommendation',
'category',
'origin',
'tags',
'motion_block',
'polls',
'id'
];
/**
* @returns a list of availavble commentSections
*/
public get commentsToExport(): ViewMotionCommentSection[] {
return this.commentRepo.getSortedViewModelList();
}
/**
* Hold the default lnMode. Will be set by the constructor.
*/
private defaultLnMode: LineNumberingMode;
/**
* Hold the default crMode. Will be set by the constructor.
*/
private defaultCrMode: ChangeRecoMode;
/**
* To deactivate the export-as-diff button
@ -87,7 +92,7 @@ export class MotionExportDialogComponent implements OnInit {
public diffVersionButton: MatButtonToggle;
/**
* To deactivate the export-as-diff button
* To deactivate the voting result button
*/
@ViewChild('votingResultButton', { static: true })
public votingResultButton: MatButtonToggle;
@ -107,10 +112,11 @@ export class MotionExportDialogComponent implements OnInit {
public formBuilder: FormBuilder,
public dialogRef: MatDialogRef<MotionExportDialogComponent>,
public configService: ConfigService,
public commentRepo: MotionCommentSectionRepositoryService
public commentRepo: MotionCommentSectionRepositoryService,
private store: StorageService
) {
this.defaultLnMode = this.configService.instant('motions_default_line_numbering');
this.defaultCrMode = this.configService.instant('motions_recommendation_text_mode');
this.defaults.lnMode = this.configService.instant('motions_default_line_numbering');
this.defaults.crMode = this.configService.instant('motions_recommendation_text_mode');
// Get the export order, exclude everything that does not count as meta-data
this.metaInfoExportOrder = motionImportExportHeaderOrder.filter(metaData => {
return !noMetaData.some(noMeta => metaData === noMeta);
@ -123,67 +129,83 @@ export class MotionExportDialogComponent implements OnInit {
* Observes the form for changes to react dynamically
*/
public ngOnInit(): void {
this.exportForm.get('format').valueChanges.subscribe((value: string) => {
// disable content for xslx
if (value === 'xlsx') {
// disable the content selection
this.exportForm.get('content').disable();
// remove the selection of "content"
this.exportForm.get('content').setValue(null);
} else {
this.exportForm.get('content').enable();
}
this.exportForm.valueChanges.pipe(auditTime(500)).subscribe((value: ExportFormData) => {
this.store.set('motion_export_selection', value);
});
if (value === 'csv' || value === 'xlsx') {
// disable and deselect "lnMode"
this.exportForm.get('lnMode').setValue(this.lnMode.None);
this.exportForm.get('lnMode').disable();
this.exportForm.get('format').valueChanges.subscribe((value: FileFormat) => this.onFormatChange(value));
}
// disable and deselect "Change Reco Mode"
// TODO: The current implementation of the motion csv export does not consider anything else than
// the "normal" motion.text, therefore this is disabled for now
this.exportForm.get('crMode').setValue(this.crMode.Original);
this.exportForm.get('crMode').disable();
/**
* React to changes on the file format
* @param format
*/
private onFormatChange(format: FileFormat): void {
// XLSX cannot have "content"
if (format === FileFormat.XLSX) {
this.disableControl('content');
} else {
this.enableControl('content');
}
this.exportForm.get('comments').disable();
if (format === FileFormat.CSV || format === FileFormat.XLSX) {
this.disableControl('lnMode');
this.disableControl('crMode');
this.disableControl('comments');
this.disableControl('pdfOptions');
// remove the selection of "Diff Version" and set it to default or original
// TODO: Use this over the disable block logic above when the export service supports more than
// just the normal motion text
// if (this.exportForm.get('crMode').value === this.crMode.Diff) {
// if (this.defaultCrMode === this.crMode.Diff) {
// this.exportForm.get('crMode').setValue(this.crMode.Original);
// } else {
// this.exportForm.get('crMode').setValue(this.defaultCrMode);
// }
// }
// remove the selection of "votingResult"
let metaInfoVal: string[] = this.exportForm.get('metaInfo').value;
// remove the selection of "votingResult"
let metaInfoVal: string[] = this.exportForm.get('metaInfo').value;
if (metaInfoVal) {
metaInfoVal = metaInfoVal.filter(info => {
return info !== 'polls';
});
this.exportForm.get('metaInfo').setValue(metaInfoVal);
// disable "Diff Version" and "Voting Result"
this.votingResultButton.disabled = true;
// TODO: CSV Issues
// this.diffVersionButton.disabled = true;
} else if (value === 'pdf') {
this.exportForm.get('comments').enable();
this.exportForm.get('lnMode').enable();
this.exportForm.get('lnMode').setValue(this.defaultLnMode);
// TODO: Temporarily necessary until CSV has been fixed
this.exportForm.get('crMode').enable();
this.exportForm.get('crMode').setValue(this.defaultCrMode);
// enable "Diff Version" and "Voting Result"
this.votingResultButton.disabled = false;
// TODO: Temporarily disabled. Will be required after CSV fixes
// this.diffVersionButton.disabled = false;
}
});
this.votingResultButton.disabled = true;
}
if (format === FileFormat.PDF) {
this.enableControl('lnMode');
this.enableControl('crMode');
this.enableControl('comments');
this.enableControl('pdfOptions');
this.votingResultButton.disabled = false;
}
}
/**
* Helper function to easier enable a control
* @param name
*/
private enableControl(name: string): void {
this.exportForm.get(name).enable();
}
/**
* Helper function to easier disable a control
*
* @param name
*/
private disableControl(name: string): void {
this.exportForm.get(name).disable();
this.exportForm.get(name).setValue(this.getOffState(name));
}
/**
* Determine what "off means in certain states"
*
* @param control
*/
private getOffState(control: string): string | null {
switch (control) {
case 'lnMode':
return this.lnMode.None;
case 'crMode':
return this.crMode.Original;
default:
return null;
}
}
/**
@ -191,13 +213,23 @@ export class MotionExportDialogComponent implements OnInit {
*/
public createForm(): void {
this.exportForm = this.formBuilder.group({
format: [this.defaultExportFormat],
lnMode: [this.defaultLnMode],
crMode: [this.defaultCrMode],
content: [this.defaultContentToExport],
metaInfo: [this.defaultInfoToExport],
format: [],
lnMode: [],
crMode: [],
content: [],
metaInfo: [],
pdfOptions: [],
comments: []
});
// restore selection or set default
this.store.get<ExportFormData>('motion_export_selection').then(restored => {
if (!!restored) {
this.exportForm.patchValue(restored);
} else {
this.exportForm.patchValue(this.defaults);
}
});
}
/**

View File

@ -15,7 +15,11 @@ import { MotionRepositoryService } from 'app/core/repositories/motions/motion-re
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { ViewTag } from 'app/site/tags/models/view-tag';
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
import { MotionExportDialogComponent } from '../motion-export-dialog/motion-export-dialog.component';
import {
MotionExportDialogComponent,
FileFormat,
ExportFormData
} from '../motion-export-dialog/motion-export-dialog.component';
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from 'app/site/motions/models/view-motion';
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
import { ViewCategory } from 'app/site/motions/models/view-category';
@ -302,19 +306,12 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
data: this.dataSource
});
exportDialogRef.afterClosed().subscribe((result: any) => {
if (result && result.format) {
exportDialogRef.afterClosed().subscribe((exportInfo: ExportFormData) => {
if (exportInfo && exportInfo.format) {
const data = this.isMultiSelect ? this.selectedRows : this.dataSource.source;
if (result.format === 'pdf') {
if (exportInfo.format === FileFormat.PDF) {
try {
this.pdfExport.exportMotionCatalog(
data,
result.lnMode,
result.crMode,
result.content,
result.metaInfo,
result.comments
);
this.pdfExport.exportMotionCatalog(data, exportInfo);
} catch (err) {
if (err instanceof PdfError) {
this.raiseError(err.message);
@ -322,10 +319,14 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
throw err;
}
}
} else if (result.format === 'csv') {
this.motionCsvExport.exportMotionList(data, [...result.content, ...result.metaInfo], result.crMode);
} else if (result.format === 'xlsx') {
this.motionXlsxExport.exportMotionList(data, result.metaInfo);
} else if (exportInfo.format === FileFormat.CSV) {
this.motionCsvExport.exportMotionList(
data,
[...exportInfo.content, ...exportInfo.metaInfo],
exportInfo.crMode
);
} else if (exportInfo.format === FileFormat.XLSX) {
this.motionXlsxExport.exportMotionList(data, exportInfo.metaInfo);
}
}
});
@ -399,11 +400,12 @@ export class MotionListComponent extends BaseListViewComponent<ViewMotion> imple
* Directly export all motions as pdf, using the current default config settings
*/
public directPdfExport(): void {
this.pdfExport.exportMotionCatalog(
this.dataSource.source,
this.configService.instant<string>('motions_default_line_numbering') as LineNumberingMode,
this.configService.instant<string>('motions_recommendation_text_mode') as ChangeRecoMode
);
const lnMode = this.configService.instant<string>('motions_default_line_numbering') as LineNumberingMode;
const crMode = this.configService.instant<string>('motions_recommendation_text_mode') as ChangeRecoMode;
this.pdfExport.exportMotionCatalog(this.dataSource.source, {
lnMode: lnMode,
crMode: crMode
});
}
/**

View File

@ -5,11 +5,12 @@ import { TranslateService } from '@ngx-translate/core';
import { CategoryRepositoryService } from 'app/core/repositories/motions/category-repository.service';
import { ConfigService } from 'app/core/ui-services/config.service';
import { MotionPdfService, InfoToExport } from './motion-pdf.service';
import { MotionPdfService } from './motion-pdf.service';
import { MotionRepositoryService } from 'app/core/repositories/motions/motion-repository.service';
import { PdfError, PdfDocumentService, StyleType, BorderType } from 'app/core/ui-services/pdf-document.service';
import { ViewCategory } from '../models/view-category';
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
import { ViewMotion } from '../models/view-motion';
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
/**
* Service to export a list of motions.
@ -55,27 +56,13 @@ export class MotionPdfCatalogService {
* @param commentsToExport
* @returns pdfmake doc definition as object
*/
public motionListToDocDef(
motions: ViewMotion[],
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object {
public motionListToDocDef(motions: ViewMotion[], exportInfo: ExportFormData): object {
let doc = [];
const motionDocList = [];
for (let motionIndex = 0; motionIndex < motions.length; ++motionIndex) {
try {
const motionDocDef: any = this.motionPdfService.motionToDocDef(
motions[motionIndex],
lnMode,
crMode,
contentToExport,
infoToExport,
commentsToExport
);
const motionDocDef: any = this.motionPdfService.motionToDocDef(motions[motionIndex], exportInfo);
// add id field to the first page of a motion to make it findable over TOC
motionDocDef[0].id = `${motions[motionIndex].id}`;
@ -95,7 +82,7 @@ export class MotionPdfCatalogService {
}
// print extra data (title, preamble, categories, toc) only if there are more than 1 motion
if (motions.length > 1) {
if (motions.length > 1 && (!exportInfo.pdfOptions || exportInfo.pdfOptions.includes('toc'))) {
doc.push(
this.pdfService.createTitle('motions_export_title'),
this.pdfService.createPreamble('motions_export_preamble'),

View File

@ -2,13 +2,14 @@ import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { MotionPdfService, InfoToExport } from './motion-pdf.service';
import { MotionPdfService } from './motion-pdf.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
import { ViewMotion } from '../models/view-motion';
import { ConfigService } from 'app/core/ui-services/config.service';
import { MotionPdfCatalogService } from './motion-pdf-catalog.service';
import { PersonalNoteContent } from 'app/shared/models/users/personal-note';
import { ViewMotionCommentSection } from '../models/view-motion-comment-section';
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
/**
* Export service to handle various kind of exporting necessities.
@ -40,8 +41,8 @@ export class MotionPdfExportService {
* @param lnMode the desired line numbering mode
* @param crMode the desired change recomendation mode
*/
public exportSingleMotion(motion: ViewMotion, lnMode?: LineNumberingMode, crMode?: ChangeRecoMode): void {
const doc = this.motionPdfService.motionToDocDef(motion, lnMode, crMode);
public exportSingleMotion(motion: ViewMotion, exportInfo?: ExportFormData): void {
const doc = this.motionPdfService.motionToDocDef(motion, exportInfo);
const filename = `${this.translate.instant('Motion')} ${motion.identifierOrTitle}`;
const metadata = {
title: filename
@ -59,27 +60,13 @@ export class MotionPdfExportService {
* @param infoToExport Determine the meta info to export
* @param commentsToExport Comments (by id) to export
*/
public exportMotionCatalog(
motions: ViewMotion[],
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): void {
const doc = this.pdfCatalogService.motionListToDocDef(
motions,
lnMode,
crMode,
contentToExport,
infoToExport,
commentsToExport
);
public exportMotionCatalog(motions: ViewMotion[], exportInfo: ExportFormData): void {
const doc = this.pdfCatalogService.motionListToDocDef(motions, exportInfo);
const filename = this.translate.instant(this.configService.instant<string>('motions_export_title'));
const metadata = {
title: filename
};
this.pdfDocumentService.download(doc, filename, metadata);
this.pdfDocumentService.download(doc, filename, metadata, exportInfo);
}
/**

View File

@ -17,6 +17,7 @@ import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { ViewMotionAmendedParagraph } from '../models/view-motion-amended-paragraph';
import { ViewMotionChangeRecommendation } from '../models/view-motion-change-recommendation';
import { ViewUnifiedChange, ViewUnifiedChangeType } from 'app/shared/models/motions/view-unified-change';
import { ExportFormData } from '../modules/motion-list/components/motion-export-dialog/motion-export-dialog.component';
/**
* Type declaring which strings are valid options for metainfos to be exported into a pdf
@ -91,14 +92,13 @@ export class MotionPdfService {
* @param commentsToExport comments to chose for export. If 'allcomments' is set in infoToExport, this selection will be ignored and all comments exported
* @returns doc def for the motion
*/
public motionToDocDef(
motion: ViewMotion,
lnMode?: LineNumberingMode,
crMode?: ChangeRecoMode,
contentToExport?: string[],
infoToExport?: InfoToExport[],
commentsToExport?: number[]
): object {
public motionToDocDef(motion: ViewMotion, exportInfo?: ExportFormData): object {
let lnMode = exportInfo && exportInfo.lnMode ? exportInfo.lnMode : null;
let crMode = exportInfo && exportInfo.crMode ? exportInfo.crMode : null;
const infoToExport = exportInfo ? exportInfo.metaInfo : null;
const contentToExport = exportInfo ? exportInfo.content : null;
let commentsToExport = exportInfo ? exportInfo.comments : null;
// get the line length from the config
const lineLength = this.configService.instant<number>('motions_line_length');
// whether to append checkboxes to follow the recommendation or not
@ -178,7 +178,13 @@ export class MotionPdfService {
const changedTitle = this.changeRecoRepo.getTitleWithChanges(motion.title, titleChange, crMode);
const identifier = motion.identifier ? ' ' + motion.identifier : '';
const title = `${this.translate.instant('Motion')} ${identifier}: ${changedTitle}`;
const pageSize = this.configService.instant('general_export_pdf_pagesize');
let title = '';
if (pageSize === 'A4') {
title += `${this.translate.instant('Motion')} `;
}
title += `${identifier}: ${changedTitle}`;
return {
text: title,