2019-01-24 14:40:05 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
|
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
|
2019-01-25 17:03:05 +01:00
|
|
|
import { ViewMotion, LineNumberingMode, ChangeRecoMode } from '../models/view-motion';
|
2019-01-24 14:40:05 +01:00
|
|
|
import { MotionPdfService } from './motion-pdf.service';
|
2019-01-31 13:40:27 +01:00
|
|
|
import { ConfigService } from 'app/core/ui-services/config.service';
|
2019-01-24 14:40:05 +01:00
|
|
|
import { Category } from 'app/shared/models/motions/category';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Service to export a list of motions.
|
|
|
|
*
|
|
|
|
* @example
|
|
|
|
* ```ts
|
|
|
|
* const docDef = this.motionPdfCatalogService.motionListToDocDef(myListOfViewMotions);
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
|
|
|
export class MotionPdfCatalogService {
|
|
|
|
/**
|
|
|
|
* Helper to add page breaks to documents
|
|
|
|
*/
|
|
|
|
private pageBreak = {
|
|
|
|
text: '',
|
|
|
|
pageBreak: 'after'
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor
|
|
|
|
*
|
|
|
|
* @param translate handle translations
|
|
|
|
* @param configService read out config variables
|
|
|
|
* @param motionPdfService handle motion to pdf conversion
|
|
|
|
*/
|
|
|
|
public constructor(
|
|
|
|
private translate: TranslateService,
|
|
|
|
private configService: ConfigService,
|
|
|
|
private motionPdfService: MotionPdfService
|
|
|
|
) {}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts the list of motions to pdfmake doc definition.
|
|
|
|
* Public entry point to conversion of multiple motions
|
|
|
|
*
|
|
|
|
* @param motions the list of view motions to convert
|
|
|
|
* @returns pdfmake doc definition as object
|
|
|
|
*/
|
2019-01-25 17:03:05 +01:00
|
|
|
public motionListToDocDef(
|
|
|
|
motions: ViewMotion[],
|
|
|
|
lnMode?: LineNumberingMode,
|
|
|
|
crMode?: ChangeRecoMode,
|
|
|
|
contentToExport?: string[],
|
|
|
|
infoToExport?: string[]
|
|
|
|
): object {
|
2019-01-24 14:40:05 +01:00
|
|
|
let doc = [];
|
|
|
|
const motionDocList = [];
|
|
|
|
|
|
|
|
for (let motionIndex = 0; motionIndex < motions.length; ++motionIndex) {
|
2019-01-25 17:03:05 +01:00
|
|
|
const motionDocDef: any = this.motionPdfService.motionToDocDef(
|
|
|
|
motions[motionIndex],
|
|
|
|
lnMode,
|
|
|
|
crMode,
|
|
|
|
contentToExport,
|
|
|
|
infoToExport
|
|
|
|
);
|
2019-01-24 14:40:05 +01:00
|
|
|
|
|
|
|
// add id field to the first page of a motion to make it findable over TOC
|
|
|
|
motionDocDef[0].id = `${motions[motionIndex].id}`;
|
|
|
|
|
|
|
|
motionDocList.push(motionDocDef);
|
|
|
|
|
|
|
|
if (motionIndex < motions.length - 1) {
|
|
|
|
motionDocList.push(this.pageBreak);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// print extra data (title, preamble, categories, toc) only if there are more than 1 motion
|
|
|
|
if (motions.length > 1) {
|
|
|
|
doc.push(this.createTitle(), this.createPreamble(), this.createToc(motions));
|
|
|
|
}
|
|
|
|
|
|
|
|
doc = doc.concat(motionDocList);
|
|
|
|
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the title for the motion list as pdfmake doc definition
|
|
|
|
*
|
|
|
|
* @returns The motion list title for the PDF document
|
|
|
|
*/
|
|
|
|
private createTitle(): object {
|
|
|
|
const titleText = this.configService.instant<string>('motions_export_title');
|
|
|
|
return {
|
|
|
|
text: titleText,
|
|
|
|
style: 'title'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the preamble for the motion list as pdfmake doc definition
|
|
|
|
*
|
|
|
|
* @returns The motion list preamble for the PDF document
|
|
|
|
*/
|
|
|
|
private createPreamble(): object {
|
|
|
|
const preambleText = this.configService.instant<string>('motions_export_preamble');
|
|
|
|
|
|
|
|
if (preambleText) {
|
|
|
|
return {
|
|
|
|
text: preambleText,
|
|
|
|
style: 'preamble'
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the table of contents for the motion book.
|
|
|
|
* Considers sorting by categories and no sorting.
|
|
|
|
*
|
|
|
|
* @param motions The motions to add in the TOC
|
|
|
|
* @param sorting The optional sorting strategy
|
|
|
|
* @returns the table of contents as document definition
|
|
|
|
*/
|
|
|
|
private createToc(motions: ViewMotion[], sorting?: string): object {
|
|
|
|
const toc = [];
|
|
|
|
const categories: Category[] = this.getUniqueCategories(motions);
|
|
|
|
|
|
|
|
// Create the toc title
|
|
|
|
const tocTitle = {
|
|
|
|
text: this.translate.instant('Table of contents'),
|
|
|
|
style: 'heading2'
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!sorting) {
|
|
|
|
sorting = this.configService.instant<string>('motions_export_category_sorting');
|
|
|
|
}
|
|
|
|
const exportCategory = sorting === 'identifier' || sorting === 'prefix';
|
|
|
|
|
|
|
|
if (exportCategory && categories) {
|
|
|
|
const catTocBody = [];
|
|
|
|
for (const category of categories) {
|
|
|
|
// push the name of the category
|
|
|
|
// make a table for correct alignment
|
|
|
|
catTocBody.push({
|
|
|
|
table: {
|
|
|
|
body: [
|
|
|
|
[
|
|
|
|
{
|
2019-01-28 16:48:49 +01:00
|
|
|
text: category.prefix ? category.prefix + ' - ' + category.name : category.name,
|
2019-01-24 14:40:05 +01:00
|
|
|
style: 'tocCategoryTitle'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
]
|
|
|
|
},
|
|
|
|
layout: 'noBorders'
|
|
|
|
});
|
|
|
|
|
|
|
|
const tocBody = motions
|
|
|
|
.filter(motion => category === motion.category)
|
|
|
|
.map(motion => this.createTocLine(motion, 'tocCategoryEntry'));
|
|
|
|
|
|
|
|
catTocBody.push(this.createTocTableDef(tocBody));
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle those without category
|
|
|
|
const uncatTocBody = motions
|
|
|
|
.filter(motion => !motion.category)
|
|
|
|
.map(motion => this.createTocLine(motion, 'tocEntry'));
|
|
|
|
|
|
|
|
// only push this array if there is at least one entry
|
|
|
|
if (uncatTocBody.length > 0) {
|
|
|
|
catTocBody.push(this.createTocTableDef(uncatTocBody));
|
|
|
|
}
|
|
|
|
|
|
|
|
toc.push(catTocBody);
|
|
|
|
} else {
|
|
|
|
// all motions in the same table
|
|
|
|
const tocBody = motions.map(motion => this.createTocLine(motion, 'tocEntry'));
|
|
|
|
toc.push(this.createTocTableDef(tocBody));
|
|
|
|
}
|
|
|
|
|
|
|
|
return [tocTitle, toc, this.pageBreak];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates the table definition for the TOC
|
|
|
|
*
|
|
|
|
* @param tocBody the body of the table
|
|
|
|
* @returns The table of contents as doc definition
|
|
|
|
*/
|
|
|
|
private createTocTableDef(tocBody: object): object {
|
|
|
|
return {
|
|
|
|
table: {
|
|
|
|
widths: ['auto', '*', 'auto'],
|
|
|
|
body: tocBody
|
|
|
|
},
|
|
|
|
layout: 'noBorders',
|
|
|
|
style: 'tocCategorySection'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a line in the TOC as list object
|
|
|
|
*
|
|
|
|
* @param motion motion to make a toc entry to
|
|
|
|
* @param style the desired style
|
|
|
|
*/
|
|
|
|
private createTocLine(motion: ViewMotion, style: string): object {
|
|
|
|
const firstColumn = motion.identifier;
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
text: firstColumn,
|
|
|
|
style: style
|
|
|
|
},
|
|
|
|
{
|
|
|
|
text: motion.title,
|
|
|
|
style: 'tocEntry'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pageReference: `${motion.id}`,
|
|
|
|
style: 'tocEntry',
|
|
|
|
alignment: 'right'
|
|
|
|
}
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the used categories from the given motion list.
|
|
|
|
*
|
|
|
|
* @param motions the list of motions
|
|
|
|
* @returns Unique list of categories
|
|
|
|
*/
|
|
|
|
private getUniqueCategories(motions: ViewMotion[]): Category[] {
|
|
|
|
const categories: Category[] = motions
|
|
|
|
// remove motions without category
|
|
|
|
.filter(motion => (motion.category ? motion : null))
|
|
|
|
// map motions their categories
|
|
|
|
.map(motion => motion.category)
|
|
|
|
// remove redundancies
|
|
|
|
.filter(
|
|
|
|
(category, index, self) =>
|
|
|
|
index ===
|
|
|
|
self.findIndex(compare => compare.prefix === category.prefix && compare.name === category.name)
|
|
|
|
);
|
|
|
|
|
|
|
|
return categories;
|
|
|
|
}
|
|
|
|
}
|