diff --git a/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts b/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts index c0063e5a1b..5657d5dae8 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/agenda.subscription.ts @@ -218,7 +218,15 @@ export const getAgendaExportSubscriptionConfig: SubscriptionConfigGenerator = (. follow: [ { idField: `content_object_id`, - fieldset: [`number`, `title`, `agenda_item_id`, `text`, `poll_ids`, ...MEETING_ROUTING_FIELDS], + fieldset: [ + `number`, + `title`, + `agenda_item_id`, + `text`, + `description`, + `poll_ids`, + ...MEETING_ROUTING_FIELDS + ], follow: [ { idField: `list_of_speakers_id`, @@ -271,6 +279,10 @@ export const getAgendaExportSubscriptionConfig: SubscriptionConfigGenerator = (. { idField: `poll_ids`, ...pollModelRequest + }, + { + idField: `motion_ids`, + fieldset: [`title`, `meeting_id`, `sequential_number`, `number`, `block_id`] } ] } diff --git a/client/src/app/site/pages/meetings/pages/agenda/services/agenda-pdf-catalog-export.service/agenda-pdf-catalog-export.service.ts b/client/src/app/site/pages/meetings/pages/agenda/services/agenda-pdf-catalog-export.service/agenda-pdf-catalog-export.service.ts index 4f689c973a..ffb0a815a5 100644 --- a/client/src/app/site/pages/meetings/pages/agenda/services/agenda-pdf-catalog-export.service/agenda-pdf-catalog-export.service.ts +++ b/client/src/app/site/pages/meetings/pages/agenda/services/agenda-pdf-catalog-export.service/agenda-pdf-catalog-export.service.ts @@ -11,9 +11,11 @@ import { TreeService } from 'src/app/ui/modules/sorting/modules/sorting-tree/ser import { MeetingPdfExportService } from '../../../../services/export'; import { MeetingSettingsService } from '../../../../services/meeting-settings.service'; +import { ViewMotion } from '../../../motions'; import { ViewPoll } from '../../../polls'; import { ViewSpeaker } from '../../modules/list-of-speakers/view-models/view-speaker'; import { ViewTopic } from '../../modules/topics/view-models'; +import { InfoToExport } from '../../pages/agenda-item-list/services/agenda-item-export.service/agenda-item-export.service'; import { ViewAgendaItem } from '../../view-models'; import { AgendaItemCommonServiceModule } from '../agenda-item-common-service.module'; @@ -57,7 +59,7 @@ export class AgendaPdfCatalogExportService { * @param pdfMeta * @returns pdfmake doc definition as object */ - public agendaListToDocDef(agendaItems: ViewAgendaItem[], exportInfo: any, pdfMeta: string[]): Content { + public agendaListToDocDef(agendaItems: ViewAgendaItem[], exportInfo: InfoToExport[], pdfMeta: string[]): Content { const addedAgendaItems = this.getMissingAgendaItems(agendaItems); this.addedAgendaItemIds = addedAgendaItems.map(item => item.id); const tree = this.treeService.makeSortedTree(agendaItems.concat(addedAgendaItems), `weight`, `parent_id`); @@ -69,10 +71,18 @@ export class AgendaPdfCatalogExportService { for (let agendaItemIndex = 0; agendaItemIndex < sortedAgendaItems.length; ++agendaItemIndex) { try { + const agendaItem: string | null = + agendaItems[agendaItemIndex].content_object?.description ?? + agendaItems[agendaItemIndex].content_object?.text ?? + null; const agendaDocDef: any = this.agendaItemToDoc(sortedAgendaItems[agendaItemIndex], exportInfo); // add id field to the first page of a agenda item to make it findable over TOC agendaDocDef[0].id = `${sortedAgendaItems[agendaItemIndex].id}`; - if (!enforcePageBreaks && agendaItemIndex + 1 < sortedAgendaItems.length) { + if ( + !enforcePageBreaks && + agendaItemIndex + 1 < sortedAgendaItems.length && + (agendaItem !== '' || agendaItem === null) + ) { if (!sortedAgendaItems[agendaItemIndex + 1].parent) { agendaDocDef.push({ text: ``, @@ -105,7 +115,7 @@ export class AgendaPdfCatalogExportService { { text: this.translate.instant(`Agenda`), style: this.getStyle(`header1`), - margin: this.getStyle(`margin-header1`) + margin: this.getStyle(`agenda-title`) } ]); @@ -231,7 +241,13 @@ export class AgendaPdfCatalogExportService { const useTitle: boolean = info.includes(`title`); const itemNumber: string = agendaItem.item_number ?? ``; const title: string = agendaItem.content_object!.getTitle(); - const styleName = agendaItem.level ? `header-child` : `header2`; + let styleName = `header2`; + let margin: string | number[] = `margin-header1`; + if (agendaItem.level > 0) { + styleName = `header-child`; + const indentationLevel = agendaItem.level * 10 + 10; + margin = [indentationLevel, 10, 0, 0]; + } let numberOrTitle = ``; if (agendaItem.content_object?.collection === `motion`) { const motion = agendaItem.content_object; @@ -252,6 +268,16 @@ export class AgendaPdfCatalogExportService { numberOrTitle = `${itemNumber} `; } numberOrTitle = numberOrTitle.concat(`${this.translate.instant('Motion block')}: ${motionBlock.title}`); + } else if (agendaItem.content_object?.collection === `assignment`) { + const election = agendaItem.content_object; + if (useItemNumber && itemNumber) { + numberOrTitle = `${itemNumber} `; + } + numberOrTitle = numberOrTitle.concat( + election.number + ? `${(this.translate.instant('Election'), election.number)}: ${election.title}` + : `${this.translate.instant('Election')}: ${election.title}` + ); } else { if (useItemNumber && itemNumber && useTitle) { numberOrTitle = `${itemNumber}: ${title}`; @@ -263,17 +289,30 @@ export class AgendaPdfCatalogExportService { } return { style: this.getStyle(styleName), - text: numberOrTitle + text: numberOrTitle, + margin: margin === 'margin-header1' ? this.getStyle(margin) : margin }; } private createTextDoc(agendaItem: ViewAgendaItem): Content { + const style = this.getStyle(`body-text`); + const margin = this.getMargin(agendaItem); + const contentObject = agendaItem.content_object; + const text = contentObject.text ?? contentObject.description; + if (!this.isTopic(agendaItem.content_object)) { - return []; - } - if (agendaItem.content_object?.getCSVExportText) { - const entry = this.htmlToPdfService.convertHtml({ htmlText: agendaItem.content_object?.text ?? `` }); - return entry; + // If not agenda topic and no motion block, it's motion or assignment + if (contentObject.collection === 'motion_block') { + return this.motionBlockEntry(contentObject, margin); + } else return this.createHtmlEntry(text, contentObject.collection, style, margin); + } else if (agendaItem.content_object?.getCSVExportText) { + // AGENDA TOPIC + const entry = this.htmlToPdfService.convertHtml({ + htmlText: + agendaItem.content_object?.text ?? + this.translate.instant(`EMPTY ${agendaItem.content_object?.collection}`) + }); + return { text: [...entry], style: style, margin }; } return []; } @@ -286,14 +325,51 @@ export class AgendaPdfCatalogExportService { const entry = this.htmlToPdfService.convertHtml({ htmlText: moderationNotes }); if (moderationNotes) { this._addExtraSpace = true; - return [ - { - text: this.translate.instant(`Moderation note`), - style: this.getStyle(`header3`), - margin: this.getStyle(`margin-header3`) - }, - entry + const moderationText = { + text: entry + }; + const text = this.translate.instant(`Moderation note`); + const style = [this.getStyle(`header3`), this.getStyle(`margin-body-text`)]; + const margin: [ + [number, number, number, number], + [number, number, number, number], + [number, number, number, number], + [number, number, number, number] + ] = [ + [20, 10, 0, 0], + [20, 10, 0, 0], + this.getStyle(`margin-header3`), + this.getStyle(`margin-moderation-note`) ]; + // Indents the moderation note inside a subitem + if (agendaItem.level > 0) { + margin[0][0] = margin[0][0] + agendaItem.level * 10; + return [ + { + text: text, + style: style[0], + margin: margin[0] + }, + { + text: moderationText, + style: style[1], + margin: margin[0] + } + ]; + } else { + return [ + { + text: text, + style: style[0], + margin: margin[2] + }, + { + text: moderationText, + style: style[1], + margin: margin[3] + } + ]; + } } else { return []; } @@ -517,27 +593,38 @@ export class AgendaPdfCatalogExportService { private getStyle(name: string): any { switch (name) { case `header1`: - return { bold: true, fontSize: 24 }; - case `header2`: return { bold: true, fontSize: 20 }; + case `header2`: + return { bold: true, fontSize: 16 }; case `header3`: - return { bold: true, fontSize: 14 }; + return { bold: true, fontSize: 12 }; case `header-child`: - return { bold: true, fontSize: 16 }; + return { bold: true, fontSize: 14 }; case `table-header`: - return { bold: true, fontSize: 12 }; + return { bold: true, fontSize: 10 }; + case `body-text`: + return { fontSize: 10 }; case `grey`: return { layout: TABLEROW_GREY }; case `italics`: return { italics: true }; - case `margin-header1`: + // [L,U,R,D] + case `agenda-title`: return [0, 0, 0, 20]; + case `margin-header1`: + return [0, 0, 0, 0]; case `margin-header3`: - return [0, 15, 0, 10]; + return [15, 10, 0, 0]; + case `margin-header-child`: + return [15, 0, 0, 0]; case `margin-type-text`: - return [0, 0, 0, 10]; + return [12, 0, 0, 0]; + case `margin-body-text`: + return [0, 10, 0, 5]; + case `margin-moderation-note`: + return [15, 10, 0, 5]; case `margin-item`: - return [0, 0, 0, 5]; + return [0, 0, 0, 0]; case `margin-item-2`: return [0, 10, 0, 5]; default: @@ -593,4 +680,39 @@ export class AgendaPdfCatalogExportService { const topic = obj as ViewTopic; return !!topic && topic.collection !== undefined && topic.collection === ViewTopic.COLLECTION && !!topic.topic; } + + public getMargin(agendaItem: ViewAgendaItem): any { + const indentationLevel = agendaItem.level * 10 + 10; + return agendaItem.level > 0 ? [indentationLevel, 10, 0, 0] : this.getStyle(`margin-body-text`); + } + + public createHtmlEntry(text: string | undefined, collection: string, style: any, margin: number[]): any { + const htmlText = text ?? this.translate.instant('EMPTY') + collection; + const entry = this.htmlToPdfService.convertHtml({ htmlText }); + return { text: [...entry], style, margin }; + } + + public motionBlockEntry(contentObject: any, margin: number[]): any { + const motions: ViewMotion[] = contentObject?.motions || []; + const adjustedMargin = [...margin]; + adjustedMargin[3] -= 5; + + if (!motions.length) { + return [ + { + text: this.translate.instant('EMPTY') + contentObject.collection, + style: this.getStyle('header3'), + margin: adjustedMargin + } + ]; + } + + return motions.map(m => ({ + text: this.htmlToPdfService.convertHtml({ + htmlText: `${this.translate.instant('Motion')} ${m.number}: ${m.title}` + }), + style: this.getStyle('header3'), + margin: adjustedMargin + })); + } }