Updated PDF layout

- use default font for pdf from vfs_font.js
  remove base64 encoding function on server side
- use recommendation config value in motion detail template
This commit is contained in:
Emanuel Schütze 2016-11-02 22:45:43 +01:00
parent 18accc58ae
commit 4e1fdc6b22
12 changed files with 483 additions and 473 deletions

View File

@ -40,7 +40,10 @@
}, },
"overrides": { "overrides": {
"pdfmake-dist-dist": { "pdfmake-dist-dist": {
"main": "build/pdfmake.min.js" "main": [
"build/pdfmake.min.js",
"build/vfs_fonts.js"
]
}, },
"pdfjs-dist": { "pdfjs-dist": {
"main": "build/pdf.combined.js" "main": "build/pdf.combined.js"

View File

@ -6,15 +6,15 @@ angular.module('OpenSlidesApp.agenda.pdf', ['OpenSlidesApp.core.pdf'])
.factory('AgendaContentProvider', [ .factory('AgendaContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { function(gettextCatalog, PDFLayout) {
var createInstance = function(items) { var createInstance = function(items) {
//use the Predefined Functions to create the title // page title
var title = PdfPredefinedFunctions.createTitle(gettextCatalog.getString("Agenda")); var title = PDFLayout.createTitle(gettextCatalog.getString("Agenda"));
//function to generate the item list out of the given "items" object // generate the item list with all subitems
var createItemList = function() { var createItemList = function() {
var agenda_items = []; var agenda_items = [];
angular.forEach(items, function (item) { angular.forEach(items, function (item) {

View File

@ -315,7 +315,7 @@ angular.module('OpenSlidesApp.agenda.site', [
}; };
$scope.makePDF = function() { $scope.makePDF = function() {
var filename = gettextCatalog.getString("Agenda")+".pdf"; var filename = gettextCatalog.getString('Agenda') + '.pdf';
var agendaContentProvider = AgendaContentProvider.createInstance($scope.items); var agendaContentProvider = AgendaContentProvider.createInstance($scope.items);
var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider); var documentProvider = PdfMakeDocumentProvider.createInstance(agendaContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); pdfMake.createPdf(documentProvider.getDocument()).download(filename);

View File

@ -6,15 +6,15 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
.factory('AssignmentContentProvider', [ .factory('AssignmentContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { function(gettextCatalog, PDFLayout) {
var createInstance = function(assignment) { var createInstance = function(assignment) {
//use the Predefined Functions to create the title // page title
var title = PdfPredefinedFunctions.createTitle(assignment.title); var title = PDFLayout.createTitle(assignment.title);
//create the preamble // number of posts
var createPreamble = function() { var createPreamble = function() {
var preambleText = gettextCatalog.getString("Number of posts to be elected") + ": "; var preambleText = gettextCatalog.getString("Number of posts to be elected") + ": ";
var memberNumber = ""+assignment.open_posts; var memberNumber = ""+assignment.open_posts;
@ -34,7 +34,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
return preamble; return preamble;
}; };
//adds the description if present in the assignment // description
var createDescription = function() { var createDescription = function() {
if (assignment.description) { if (assignment.description) {
var descriptionText = gettextCatalog.getString("Description") + ":"; var descriptionText = gettextCatalog.getString("Description") + ":";
@ -56,7 +56,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
} }
}; };
//creates the candidate list in columns if the assignment phase is 'voting' // show candidate list (if assignment phase is not 'finished')
var createCandidateList = function() { var createCandidateList = function() {
if (assignment.phase != 2) { if (assignment.phase != 2) {
var candidatesText = gettextCatalog.getString("Candidates") + ": "; var candidatesText = gettextCatalog.getString("Candidates") + ": ";
@ -90,23 +90,23 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
} }
}; };
//handles the case if a candidate is elected or not // handles the case if a candidate is elected or not
var electedCandidateLine = function(candidateName, pollOption, pollTableBody) { var electedCandidateLine = function(candidateName, pollOption, pollTableBody) {
if (pollOption.is_elected) { if (pollOption.is_elected) {
return { return {
text: candidateName + "*", text: candidateName + "*",
bold: true, bold: true,
style: PdfPredefinedFunctions.flipTableRowStyle(pollTableBody.length) style: PDFLayout.flipTableRowStyle(pollTableBody.length)
}; };
} else { } else {
return { return {
text: candidateName, text: candidateName,
style: PdfPredefinedFunctions.flipTableRowStyle(pollTableBody.length) style: PDFLayout.flipTableRowStyle(pollTableBody.length)
}; };
} }
}; };
//creates the pull result table // creates the election result table
var createPollResultTable = function() { var createPollResultTable = function() {
var resultBody = []; var resultBody = [];
angular.forEach(assignment.polls, function(poll, pollIndex) { angular.forEach(assignment.polls, function(poll, pollIndex) {
@ -144,7 +144,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
electedCandidateLine(candidateName, pollOption, pollTableBody), electedCandidateLine(candidateName, pollOption, pollTableBody),
{ {
text: votes[0].value + " " + votes[0].percentStr, text: votes[0].value + " " + votes[0].percentStr,
style: PdfPredefinedFunctions.flipTableRowStyle(pollTableBody.length) style: PDFLayout.flipTableRowStyle(pollTableBody.length)
} }
]); ]);
} else if (poll.pollmethod == 'yn') { } else if (poll.pollmethod == 'yn') {
@ -163,7 +163,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
votes[1].percentStr votes[1].percentStr
} }
], ],
style: PdfPredefinedFunctions.flipTableRowStyle(pollTableBody.length) style: PDFLayout.flipTableRowStyle(pollTableBody.length)
} }
]); ]);
} else if (poll.pollmethod == 'yna') { } else if (poll.pollmethod == 'yna') {
@ -187,7 +187,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
votes[2].percentStr votes[2].percentStr
} }
], ],
style: PdfPredefinedFunctions.flipTableRowStyle(pollTableBody.length) style: PDFLayout.flipTableRowStyle(pollTableBody.length)
} }
]); ]);
} }
@ -247,7 +247,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
} }
}); });
//Add the legend to the result body // add the legend to the result body
if (assignment.polls.length > 0) { if (assignment.polls.length > 0) {
resultBody.push({ resultBody.push({
text: "* = " + gettextCatalog.getString("is elected"), text: "* = " + gettextCatalog.getString("is elected"),
@ -281,12 +281,12 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
.factory('BallotContentProvider', [ .factory('BallotContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { function(gettextCatalog, PDFLayout) {
var createInstance = function(scope, poll, pollNumber) { var createInstance = function(scope, poll, pollNumber) {
// use the Predefined Functions to create the title // page title
var createTitle = function() { var createTitle = function() {
return { return {
text: scope.assignment.title, text: scope.assignment.title,
@ -294,7 +294,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
}; };
}; };
//function to create the poll hint // poll description
var createPollHint = function() { var createPollHint = function() {
var description = poll.description ? ': ' + poll.description : ''; var description = poll.description ? ': ' + poll.description : '';
return { return {
@ -303,19 +303,19 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
}; };
}; };
//function to create the selection entries // election entries
var createYNBallotEntry = function(decision) { var createYNBallotEntry = function(decision) {
var YNColumn = [ var YNColumn = [
{ {
width: "auto", width: "auto",
stack: [ stack: [
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("Yes")) PDFLayout.createBallotEntry(gettextCatalog.getString("Yes"))
] ]
}, },
{ {
width: "auto", width: "auto",
stack: [ stack: [
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("No")) PDFLayout.createBallotEntry(gettextCatalog.getString("No"))
] ]
}, },
]; ];
@ -324,7 +324,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
YNColumn.push({ YNColumn.push({
width: "auto", width: "auto",
stack: [ stack: [
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("Abstain")) PDFLayout.createBallotEntry(gettextCatalog.getString("Abstain"))
] ]
}); });
} }
@ -346,7 +346,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
if (poll.pollmethod == 'votes') { if (poll.pollmethod == 'votes') {
angular.forEach(poll.options, function(option) { angular.forEach(poll.options, function(option) {
var candidate = option.candidate.get_full_name(); var candidate = option.candidate.get_full_name();
candidateBallotList.push(PdfPredefinedFunctions.createBallotEntry(candidate)); candidateBallotList.push(PDFLayout.createBallotEntry(candidate));
}); });
} else { } else {
angular.forEach(poll.options, function(option) { angular.forEach(poll.options, function(option) {
@ -361,7 +361,6 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
// since it is not possible to give a column a fixed height, we draw an "empty" column // since it is not possible to give a column a fixed height, we draw an "empty" column
// with a one px width and a fixed top-margin // with a one px width and a fixed top-margin
return { return {
columns : [ columns : [
{ {
@ -431,7 +430,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
widths: ['50%', '50%'], widths: ['50%', '50%'],
body: tableBody body: tableBody
}, },
layout: PdfPredefinedFunctions.getBallotLayoutLines() layout: PDFLayout.getBallotLayoutLines()
}]; }];
}; };
@ -451,13 +450,13 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
.factory('AssignmentCatalogContentProvider', [ .factory('AssignmentCatalogContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
'Config', 'Config',
function(gettextCatalog, PdfPredefinedFunctions, Config) { function(gettextCatalog, PDFLayout, Config) {
var createInstance = function(allAssignmnets) { var createInstance = function(allAssignmnets) {
var title = PdfPredefinedFunctions.createTitle( var title = PDFLayout.createTitle(
gettextCatalog.getString(Config.get('assignments_pdf_title').value) gettextCatalog.getString(Config.get('assignments_pdf_title').value)
); );
@ -476,7 +475,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
var createTOContent = function(assignmentTitles) { var createTOContent = function(assignmentTitles) {
var heading = { var heading = {
text: gettextCatalog.getString("Table of contents"), text: gettextCatalog.getString("Table of contents"),
style: "heading", style: "heading2",
}; };
var toc = []; var toc = [];
@ -490,7 +489,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
return [ return [
heading, heading,
toc, toc,
PdfPredefinedFunctions.addPageBreak() PDFLayout.addPageBreak()
]; ];
}; };
@ -503,7 +502,7 @@ angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf'])
assignmentTitles.push(assignment.title); assignmentTitles.push(assignment.title);
assignmentContent.push(assignment.getContent()); assignmentContent.push(assignment.getContent());
if (key < allAssignmnets.length - 1) { if (key < allAssignmnets.length - 1) {
assignmentContent.push(PdfPredefinedFunctions.addPageBreak()); assignmentContent.push(PDFLayout.addPageBreak());
} }
}); });

View File

@ -4,23 +4,51 @@
angular.module('OpenSlidesApp.core.pdf', []) angular.module('OpenSlidesApp.core.pdf', [])
.factory('PdfPredefinedFunctions', [ /*
* General layout functions for building PDFs with pdfmake.
*/
.factory('PDFLayout', [
function() { function() {
var PdfPredefinedFunctions = {}; var PDFLayout = {};
var BallotCircleDimensions = { var BallotCircleDimensions = {
yDistance: 6, yDistance: 6,
size: 8 size: 8
}; };
PdfPredefinedFunctions.createTitle = function(titleString) { // Set and return default font family.
//
// To use custom ttf font files you have to replace the vfs_fonts.js file.
// See https://github.com/pdfmake/pdfmake/wiki/Custom-Fonts---client-side
PDFLayout.getFontName = function() {
pdfMake.fonts = {
Roboto: {
normal: 'Roboto-Regular.ttf',
bold: 'Roboto-Medium.ttf',
italics: 'Roboto-Italic.ttf',
bolditalics: 'Roboto-Italic.ttf'
}
};
return "Roboto";
};
// page title
PDFLayout.createTitle = function(title) {
return { return {
text: titleString, text: title,
style: "title" style: "title"
}; };
}; };
// function to apply a pagebreak-keyword // page subtitle
PdfPredefinedFunctions.addPageBreak = function() { PDFLayout.createSubtitle = function(subtitle) {
return {
text: subtitle,
style: "subtitle"
};
};
// pagebreak
PDFLayout.addPageBreak = function() {
return [ return [
{ {
text: '', text: '',
@ -29,7 +57,8 @@ angular.module('OpenSlidesApp.core.pdf', [])
]; ];
}; };
PdfPredefinedFunctions.flipTableRowStyle = function(currentTableSize) { // table row style
PDFLayout.flipTableRowStyle = function(currentTableSize) {
if (currentTableSize % 2 === 0) { if (currentTableSize % 2 === 0) {
return "tableEven"; return "tableEven";
} else { } else {
@ -37,8 +66,8 @@ angular.module('OpenSlidesApp.core.pdf', [])
} }
}; };
//draws a circle // draws a circle
PdfPredefinedFunctions.drawCircle = function(y, size) { PDFLayout.drawCircle = function(y, size) {
return [ return [
{ {
type: 'ellipse', type: 'ellipse',
@ -51,14 +80,15 @@ angular.module('OpenSlidesApp.core.pdf', [])
]; ];
}; };
//Returns an entry in the ballot with a circle to draw into // returns an entry in the ballot with a circle to draw into
PdfPredefinedFunctions.createBallotEntry = function(decision) { PDFLayout.createBallotEntry = function(decision) {
return { return {
margin: [40+BallotCircleDimensions.size, 10, 0, 0], margin: [40+BallotCircleDimensions.size, 10, 0, 0],
columns: [ columns: [
{ {
width: 15, width: 15,
canvas: PdfPredefinedFunctions.drawCircle(BallotCircleDimensions.yDistance, BallotCircleDimensions.size) canvas: PDFLayout.drawCircle(BallotCircleDimensions.yDistance,
BallotCircleDimensions.size)
}, },
{ {
width: "auto", width: "auto",
@ -68,7 +98,8 @@ angular.module('OpenSlidesApp.core.pdf', [])
}; };
}; };
PdfPredefinedFunctions.getBallotLayoutLines = function() { // crop marks for ballot papers
PDFLayout.getBallotLayoutLines = function() {
return { return {
hLineWidth: function(i, node) { hLineWidth: function(i, node) {
return (i === 0 || i === node.table.body.length) ? 0 : 0.5; return (i === 0 || i === node.table.body.length) ? 0 : 0.5;
@ -85,10 +116,11 @@ angular.module('OpenSlidesApp.core.pdf', [])
}; };
}; };
return PdfPredefinedFunctions; return PDFLayout;
} }
]) ])
.factory('HTMLValidizer', function() { .factory('HTMLValidizer', function() {
var HTMLValidizer = {}; var HTMLValidizer = {};
@ -111,138 +143,163 @@ angular.module('OpenSlidesApp.core.pdf', [])
return HTMLValidizer; return HTMLValidizer;
}) })
.factory('PdfMakeDocumentProvider', [ .factory('PdfMakeDocumentProvider', [
'gettextCatalog', 'gettextCatalog',
'Config', 'Config',
function(gettextCatalog, Config) { 'PDFLayout',
function(gettextCatalog, Config, PDFLayout) {
/** /**
* Provides the global Document * Provides the global document
* @constructor * @constructor
* @param {object} contentProvider - Object with on method `getContent`, which returns an array for content * @param {object} contentProvider - Object with on method `getContent`, which
* @param {string} defaultFont - Default font for the document * returns an array for content
*/ */
var createInstance = function(contentProvider, defaultFont) { var createInstance = function(contentProvider) {
/** // PDF header
* Generates header for PDF
* @constructor
*/
var header = function() { var header = function() {
var date = new Date(); var date = new Date();
return { var columns = [];
// alignment: 'center',
color: '#555', // add here your custom logo (which has to be added to a custom vfs_fonts.js)
fontSize: 10, // see https://github.com/pdfmake/pdfmake/wiki/Custom-Fonts---client-side
margin: [80, 50, 80, 0], //margin: [left, top, right, bottom] /*
columns: [ columns.push({
{ image: 'logo.png',
text: Config.get('general_event_name').value + ' · ' + Config.get('general_event_description').value , fit: [180,40]
fontSize:10, });*/
width: '70%'
}, var line1 = [
{ Config.get('general_event_name').value,
fontSize: 6, Config.get('general_event_description').value
width: '30%', ].join(' ');
text: gettextCatalog.getString('As of') + " " + date.toLocaleDateString() + " " + date.toLocaleTimeString(), var line2 = [
alignment: 'right' Config.get('general_event_location').value,
}] Config.get('general_event_date').value
}; ].join(', ');
}, var text = [line1, line2].join('\n');
/** columns.push({
* Generates footer line text: text,
* @function fontSize:10,
* @param {object} currentPage - An object representing the current page width: '100%'
* @param {number} pageCount - number for pages });
*/ return {
footer = function(currentPage, pageCount) { color: '#555',
return { fontSize: 9,
alignment: 'center', margin: [80, 30, 80, 10], // [left, top, right, bottom]
fontSize: 8, columns: columns,
color: '#555', columnGap: 10
text: gettextCatalog.getString('Page') + ' ' + currentPage.toString() + ' / ' + pageCount.toString()
};
},
/**
* Generates the document(definition) for pdfMake
* @function
*/
getDocument = function() {
var content = contentProvider.getContent();
return {
pageSize: 'A4',
pageMargins: [80, 90, 80, 60],
defaultStyle: {
font: defaultFont,
fontSize: 10
},
header: header,
footer: footer,
content: content,
styles: {
title: {
fontSize: 30,
margin: [0,0,0,20],
bold: true
},
preamble: {
fontSize: 12,
margin: [0,0,0,10],
},
userDataTitle: {
fontSize: 26,
margin: [0,0,0,0],
bold: true
},
textItem: {
fontSize: 11,
margin: [0,7]
},
heading: {
fontSize: 16,
margin: [0,0,0,10],
bold: true
},
userDataHeading: {
fontSize: 14,
margin: [0,10],
bold: true
},
userDataTopic: {
fontSize: 12,
margin: [0,5]
},
userDataValue: {
fontSize: 12,
margin: [15,5]
},
tableofcontent: {
fontSize: 12,
margin: [0,3]
},
listParent: {
fontSize: 14,
margin: [0,5]
},
listChild: {
fontSize: 11,
margin: [0,5]
},
tableHeader: {
bold: true,
fillColor: 'white'
},
tableEven: {
fillColor: 'white'
},
tableOdd: {
fillColor: '#eee'
},
tableConclude: {
fillColor: '#ddd',
bold: true
}
}
};
}; };
};
// PDF footer
var footer = function(currentPage, pageCount) {
return {
alignment: 'center',
fontSize: 8,
color: '#555',
text: gettextCatalog.getString('Page') + ' ' + currentPage.toString() +
' / ' + pageCount.toString()
};
};
// Generates the document(definition) for pdfMake
var getDocument = function() {
var content = contentProvider.getContent();
return {
pageSize: 'A4',
pageMargins: [80, 90, 80, 60],
defaultStyle: {
font: PDFLayout.getFontName(),
fontSize: 10
},
header: header,
footer: footer,
content: content,
styles: {
title: {
fontSize: 18,
margin: [0,0,0,20],
bold: true
},
subtitle: {
fontSize: 9,
margin: [0,-20,0,20],
},
preamble: {
fontSize: 10,
margin: [0,0,0,10],
},
userDataTitle: {
fontSize: 26,
margin: [0,0,0,0],
bold: true
},
textItem: {
fontSize: 11,
margin: [0,7]
},
heading2: {
fontSize: 14,
margin: [0,0,0,10],
bold: true
},
heading3: {
fontSize: 12,
margin: [0,10,0,0],
bold: true
},
userDataHeading: {
fontSize: 14,
margin: [0,10],
bold: true
},
userDataTopic: {
fontSize: 12,
margin: [0,5]
},
userDataValue: {
fontSize: 12,
margin: [15,5]
},
tableofcontent: {
fontSize: 12,
margin: [0,3]
},
listParent: {
fontSize: 12,
margin: [0,5]
},
listChild: {
fontSize: 10,
margin: [0,5]
},
tableHeader: {
bold: true,
fillColor: 'white'
},
tableEven: {
fillColor: 'white'
},
tableOdd: {
fillColor: '#eee'
},
tableConclude: {
fillColor: '#ddd',
bold: true
},
grey: {
fillColor: '#ddd',
},
lightgrey: {
fillColor: '#aaa',
},
bold: {
bold: true,
}
}
};
};
return { return {
getDocument: getDocument getDocument: getDocument
}; };
@ -256,14 +313,14 @@ angular.module('OpenSlidesApp.core.pdf', [])
.factory('PdfMakeBallotPaperProvider', [ .factory('PdfMakeBallotPaperProvider', [
'gettextCatalog', 'gettextCatalog',
'Config', 'Config',
function(gettextCatalog, Config) { 'PDFLayout',
function(gettextCatalog, Config, PDFLayout) {
/** /**
* Provides the global Document * Provides the global Document
* @constructor * @constructor
* @param {object} contentProvider - Object with on method `getContent`, which returns an array for content * @param {object} contentProvider - Object with on method `getContent`, which returns an array for content
* @param {string} defaultFont - Default font for the document
*/ */
var createInstance = function(contentProvider, defaultFont) { var createInstance = function(contentProvider) {
/** /**
* Generates the document(definition) for pdfMake * Generates the document(definition) for pdfMake
* @function * @function
@ -274,7 +331,7 @@ angular.module('OpenSlidesApp.core.pdf', [])
pageSize: 'A4', pageSize: 'A4',
pageMargins: [0, 0, 0, 0], pageMargins: [0, 0, 0, 0],
defaultStyle: { defaultStyle: {
font: defaultFont, font: PDFLayout.getFontName(),
fontSize: 10 fontSize: 10
}, },
content: content, content: content,
@ -308,10 +365,9 @@ angular.module('OpenSlidesApp.core.pdf', [])
* Converter component for HTML->JSON for pdfMake * Converter component for HTML->JSON for pdfMake
* @constructor * @constructor
* @param {object} images - Key-Value structure representing image.src/BASE64 of images * @param {object} images - Key-Value structure representing image.src/BASE64 of images
* @param {object} fonts - Key-Value structure representing fonts (detailed description below)
* @param {object} pdfMake - the converter component enhances pdfMake * @param {object} pdfMake - the converter component enhances pdfMake
*/ */
var createInstance = function(images, fonts, pdfMake) { var createInstance = function(images, pdfMake) {
var slice = Function.prototype.call.bind([].slice), var slice = Function.prototype.call.bind([].slice),
map = Function.prototype.call.bind([].map), map = Function.prototype.call.bind([].map),
@ -319,47 +375,6 @@ angular.module('OpenSlidesApp.core.pdf', [])
DIFF_MODE_INSERT = 1, DIFF_MODE_INSERT = 1,
DIFF_MODE_DELETE = 2, DIFF_MODE_DELETE = 2,
/**
* Adds a custom font to pdfMake.vfs
* @function
* @param {object} fontFiles - object with Files to add to pdfMake.vfs
* {
* normal: $Filename
* bold: $Filename
* italics: $Filename
* bolditalics: $Filename
* }
*/
addFontToVfs = function(fontFiles) {
Object.keys(fontFiles).forEach(function(name) {
var file = fontFiles[name];
pdfMake.vfs[file.name] = file.content;
});
},
/**
* Adds custom fonts to pdfMake
* @function
* @param {object} fontInfo - Font configuration from Backend
* {
* $FontName : {
* normal: $Filename
* bold: $Filename
* italics: $Filename
* bolditalics: $Filename
* }
* }
*/
registerFont = function(fontInfo) {
Object.keys(fontInfo).forEach(function(name) {
var font = fontInfo[name];
addFontToVfs(font);
pdfMake.fonts = pdfMake.fonts || {};
pdfMake.fonts[name] = Object.keys(font).reduce(function(fontDefinition, style) {
fontDefinition[style] = font[style].name;
return fontDefinition;
}, {});
});
},
/** /**
* Convertes HTML for use with pdfMake * Convertes HTML for use with pdfMake
* @function * @function
@ -726,9 +741,6 @@ angular.module('OpenSlidesApp.core.pdf', [])
o[name] = content; o[name] = content;
return o; return o;
}; };
fonts.forEach(function(fontInfo) {
registerFont(fontInfo);
});
return { return {
convertHTML: convertHTML, convertHTML: convertHTML,
createElement: create createElement: create

View File

@ -788,22 +788,12 @@ class MediaEncoder(utils_views.APIView):
Takes an array of IMG.src - Paths Takes an array of IMG.src - Paths
Retrieves the according images Retrieves the according images
Encodes the images to BASE64 Encodes the images to BASE64
Add configured fonts
Puts it into a key-value structure Puts it into a key-value structure
{ {
"images": { "images": {
"media/file/ubuntu.png":"$ENCODED_IMAGE" "media/file/ubuntu.png":"$ENCODED_IMAGE"
}, }
"fonts": [{
$FontName : {
normal: $Filename
bold: $Filename
italics: $Filename
bolditalics: $Filename
}
}],
"default_font": "$DEFAULTFONT"
} }
:param request: :param request:
@ -818,86 +808,10 @@ class MediaEncoder(utils_views.APIView):
body_unicode = request.body.decode('utf-8') body_unicode = request.body.decode('utf-8')
file_paths = json.loads(body_unicode) file_paths = json.loads(body_unicode)
images = {file_path: self.encode_image_from(file_path) for file_path in file_paths} images = {file_path: self.encode_image_from(file_path) for file_path in file_paths}
fonts = self.encoded_fonts()
default_font = self.get_default_font()
return Response({ return Response({
"images": images, "images": images
"fonts": fonts,
"defaultFont": default_font
}) })
def get_default_font(self):
"""
Returns the default font for pdfMake.
Note: For development purposes this is hard coded.
:return: the name of the default Font
"""
return 'OpenSans'
def encoded_fonts(self):
"""
Generate font encoding for pdfMake
:return: list of Font Encodings
"""
fonts = self.get_configured_fonts()
enc_fonts = [self.encode_font(name, files) for name, files in fonts.items()]
return enc_fonts
def get_configured_fonts(self):
"""
Returns the configured fonts
Note: For development purposes, the current font definition is hard coded
The form is {
$FontName : {
normal: $Filename
bold: $Filename
italics: $Filename
bolditalics: $Filename
}
}
This structure is required according to PDFMake specs.
:return:
"""
fonts = {
'OpenSans': {
'normal': 'OpenSans-Regular.ttf',
'bold': 'OpenSans-Bold.ttf',
'italics': 'OpenSans-Italic.ttf',
'bolditalics': 'OpenSans-BoldItalic.ttf'
}
}
return fonts
def encode_font(self, font_name, font_files):
"""
Responsible to encode a single font
:param fontName: name of the font
:param font_files: files for different weighs
:return: dictionary with encoded font
"""
encoded_files = {type: self.encode_font_from(file_path) for type, file_path in font_files.items()}
return {font_name: encoded_files}
def encode_font_from(self, file_path):
"""
Returns the BASE64 encoded version of an image-file for a given path
:param file_path:
:return: dictionary with the string representation (content) and the name of the file
for the pdfMake.vfs structure
"""
path = os.path.join(settings.MODULE_DIR, 'static', 'fonts', os.path.basename(file_path))
try:
with open(path, "rb") as file:
string_representation = "{}".format(base64.b64encode(file.read()).decode())
except:
return ""
else:
return {"content": string_representation, "name": file_path}
def encode_image_from(self, file_path): def encode_image_from(self, file_path):
""" """
Returns the BASE64 encoded version of an image-file for a given path Returns the BASE64 encoded version of an image-file for a given path

View File

@ -109,7 +109,7 @@ angular.module('OpenSlidesApp.motions', [
} }
// calculate percent value // calculate percent value
var config = Config.get('motions_poll_100_percent_base').value; var config = Config.get('motions_poll_100_percent_base').value;
var percentStr; var percentStr = '';
var percentNumber = null; var percentNumber = null;
var base = null; var base = null;
if (!impossible) { if (!impossible) {
@ -137,7 +137,8 @@ angular.module('OpenSlidesApp.motions', [
return { return {
'value': value, 'value': value,
'percentStr': percentStr, 'percentStr': percentStr,
'percentNumber': percentNumber 'percentNumber': percentNumber,
'display': value + ' ' + percentStr
}; };
} }
} }

View File

@ -30,9 +30,9 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions',
}); });
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) { $http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake); var converter = PdfMakeConverter.createInstance(data.images, pdfMake);
var motionContentProvider = MotionContentProvider.createInstance(converter, $scope.motion, $scope, User, $http); var motionContentProvider = MotionContentProvider.createInstance(converter, $scope.motion, $scope, User, $http);
var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider, data.defaultFont); var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider);
var filename = gettextCatalog.getString("Motion") + "-" + $scope.motion.identifier + ".pdf"; var filename = gettextCatalog.getString("Motion") + "-" + $scope.motion.identifier + ".pdf";
pdfMake.createPdf(documentProvider.getDocument()).download(filename); pdfMake.createPdf(documentProvider.getDocument()).download(filename);
}); });

View File

@ -6,21 +6,199 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
.factory('MotionContentProvider', [ .factory('MotionContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { 'Category',
'Config',
function(gettextCatalog, PDFLayout, Category, Config) {
/** /**
* Provides the content as JS objects for Motions in pdfMake context * Provides the content as JS objects for Motions in pdfMake context
* @constructor * @constructor
*/ */
var createInstance = function(converter, motion, $scope, User) { var createInstance = function(converter, motion, $scope) {
// title
var identifier = motion.identifier ? ' ' + motion.identifier : ''; var identifier = motion.identifier ? ' ' + motion.identifier : '';
var header = PdfPredefinedFunctions.createTitle(gettextCatalog.getString("Motion") + identifier + var title = PDFLayout.createTitle(
': ' + motion.getTitle($scope.version)); gettextCatalog.getString('Motion') + identifier + ': ' +
motion.getTitle($scope.version)
);
// generates the text of the motion. Also septerates between line-numbers // subtitle
var textContent = function() { var subtitle = PDFLayout.createSubtitle(
gettextCatalog.getString('Sequential number') + ': ' + motion.id
);
// meta data table
var metaTable = function() {
var metaTableBody = [];
// submitters
var submitters = _.map(motion.submitters, function (submitter) {
return submitter.get_full_name();
}).join(', ');
metaTableBody.push([
{
text: gettextCatalog.getString('Submitters') + ':',
style: ['bold', 'grey']
},
{
text: submitters,
style: 'grey'
}
]);
// state
metaTableBody.push([
{
text: gettextCatalog.getString('State') + ':',
style: ['bold', 'grey']
},
{
text: motion.getStateName(),
style: 'grey'
}
]);
// recommendation
if (motion.getRecommendationName()) {
metaTableBody.push([
{
text: Config.get('motions_recommendations_by').value + ':',
style: ['bold', 'grey']
},
{
text: motion.getRecommendationName(),
style: 'grey'
}
]);
}
// category
if (motion.category) {
metaTableBody.push([
{
text: gettextCatalog.getString('Category') + ':',
style: ['bold', 'grey']
},
{
text: motion.category.name,
style: 'grey'
}
]);
}
// voting result
var column1 = [];
var column2 = [];
var column3 = [];
motion.polls.map(function(poll, index) {
var votenumber = '';
if (motion.polls.length > 1) {
votenumber = index + 1 + '. ' + gettextCatalog.getString('Vote');
}
// yes
var yes = poll.getVote(poll.yes, 'yes');
column1.push(gettextCatalog.getString('Yes') + ':');
column2.push(yes.value);
column3.push(yes.percentStr);
// no
var no = poll.getVote(poll.no, 'no');
column1.push(gettextCatalog.getString('No') + ':');
column2.push(no.value);
column3.push(no.percentStr);
// abstain
var abstain = poll.getVote(poll.abstain, 'abstain');
column1.push(gettextCatalog.getString('Abstain') + ':');
column2.push(abstain.value);
column3.push(abstain.percentStr);
// votes valid
if (poll.votesvalid) {
var valid = poll.getVote(poll.votesvalid, 'votesvalid');
column1.push(gettextCatalog.getString('Valid votes') + ':');
column2.push(valid.value);
column3.push(valid.percentStr);
}
// votes invalid
if (poll.votesvalid) {
var invalid = poll.getVote(poll.votesinvalid, 'votesinvalid');
column1.push(gettextCatalog.getString('Invalid votes') + ':');
column2.push(invalid.value);
column3.push(invalid.percentStr);
}
// votes cast
if (poll.votescast) {
var cast = poll.getVote(poll.votescast, 'votescast');
column1.push(gettextCatalog.getString('Votes cast') + ':');
column2.push(cast.value);
column3.push(cast.percentStr);
}
});
metaTableBody.push([
{
text: gettextCatalog.getString('Voting result') + ':',
style: ['bold', 'grey']
},
{
columns: [
{
text: column1.join('\n'),
width: 'auto'
},
{
text: column2.join('\n'),
width: 'auto',
alignment: 'right'
},
{
text: column3.join('\n'),
width: 'auto',
alignment: 'right'
},
],
columnGap: 7,
style: 'grey'
}
]);
// build table
var metaTableJsonString = {
table: {
widths: ['30%','70%'],
body: metaTableBody,
},
margin: [0, 0, 0, 20],
layout: {
hLineWidth: function(i, node) {
return (i === 0 || i === node.table.body.length) ? 0 : 0.5;
},
vLineWidth: function(i, node) {
return (i === 0 || i === node.table.widths.length) ? 0 : 0;
},
hLineColor: function(i, node) {
return (i === 0 || i === node.table.body.length) ? '' : 'white';
},
vLineColor: function(i, node) {
return (i === 0 || i === node.table.widths.length) ? '' : 'white';
}
}
};
return metaTableJsonString;
};
// motion title
var motionTitle = function() {
return [{
text: motion.getTitle($scope.version),
style: 'heading3'
}];
};
// motion text (with line-numbers)
var motionText = function() {
if ($scope.lineNumberMode == "inline" || $scope.lineNumberMode == "outside") { if ($scope.lineNumberMode == "inline" || $scope.lineNumberMode == "outside") {
/* in order to distinguish between the line-number-types we need to pass the scope /* in order to distinguish between the line-number-types we need to pass the scope
* to the convertHTML function. * to the convertHTML function.
@ -35,109 +213,18 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
} }
}; };
// Generate text of reason // motion reason heading
var reasonContent = function() { var motionReason = function() {
return converter.convertHTML(motion.getReason($scope.version), $scope); var reason = [{
text: gettextCatalog.getString('Reason'),
style: 'heading3'
}];
reason.push(converter.convertHTML(motion.getReason($scope.version), $scope));
return reason;
}; };
// Generate text of signment
var signment = function() {
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n' + motion.getStateName());
state.width = "70%";
label.width = "30%";
label.bold = true;
var signment = converter.createElement("columns", [label, state]);
signment.margin = [10, 20, 0, 10];
signment.lineHeight = 2.5;
return signment;
};
// Generates polls // getters
var polls = function() {
if (!motion.polls.length) return {};
var pollLabel = converter.createElement("text", gettextCatalog.getString('Voting result') + ":"),
results = function() {
return motion.polls.map(function(poll, index) {
var id = index + 1,
yes = poll.yes ? poll.yes : '-', // if no poll.yes is given set it to '-'
yesRelative = poll.getVote(poll.yes, 'yes').percentStr,
no = poll.no ? poll.no : '-',
noRelative = poll.getVote(poll.no, 'no').percentStr,
abstain = poll.abstain ? poll.abstain : '-',
abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
valid = poll.votesvalid ? poll.votesvalid : '-',
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
number = {
text: id + ".",
width: "5%"
},
headerText = {
text: gettextCatalog.getString('Vote'),
width: "15%"
},
/**
* Generates a part (consisting of different columns) of the polls
*
* Example Ja 100 ( 90% )
*
* @function
* @param {string} name - E.g. "Ja"
* @param {number} value - E.g.100
* @param {number} relValue - E.g. 90
*/
createPart = function(name, value, relValue) {
var indexColumn = converter.createElement("text");
var nameColumn = converter.createElement("text", "" + name);
var valueColumn = converter.createElement("text", "" + value);
var relColumn = converter.createElement("text", relValue);
valueColumn.width = "40%";
indexColumn.width = "5%";
valueColumn.width = "5%";
valueColumn.alignment = "right";
relColumn.margin = [5, 0, 0, 0];
return [indexColumn, nameColumn, valueColumn, relColumn];
},
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid votes"), valid, validRelative)),
heading = converter.createElement("columns", [number, headerText]),
pollResult = converter.createElement("stack", [
heading, yesPart, noPart, abstainPart, totalPart
]);
return pollResult;
}, {});
};
pollLabel.width = '35%';
pollLabel.bold = true;
var result = converter.createElement("columns", [pollLabel, results()]);
result.margin = [10, 0, 0, 10];
result.lineHeight = 1;
return result;
};
//Generates title section for motion
var titleSection = function() {
var title = converter.createElement("text", motion.getTitle($scope.version));
title.bold = true;
title.fontSize = 14;
title.margin = [0, 0, 0, 10];
return title;
};
// Generates reason section for polls
var reason = function() {
var r = converter.createElement("text", gettextCatalog.getString("Reason") + ":");
r.bold = true;
r.fontSize = 14;
r.margin = [0, 30, 0, 10];
return r;
};
//getters
var getTitle = function() { var getTitle = function() {
return motion.getTitle($scope.verion); return motion.getTitle($scope.verion);
}; };
@ -152,25 +239,17 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
// Generates content as a pdfmake consumable // Generates content as a pdfmake consumable
var getContent = function() { var getContent = function() {
if (reasonContent().length === 0 ) { var content = [
return [ title,
header, subtitle,
signment(), metaTable(),
polls(), motionTitle(),
titleSection(), motionText(),
textContent(), ];
]; if (motionReason()) {
} else { content.push(motionReason());
return [
header,
signment(),
polls(),
titleSection(),
textContent(),
reason(),
reasonContent()
];
} }
return content;
}; };
return { return {
getContent: getContent, getContent: getContent,
@ -186,8 +265,8 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
}]) }])
.factory('PollContentProvider', [ .factory('PollContentProvider', [
'PdfPredefinedFunctions', 'PDFLayout',
function(PdfPredefinedFunctions) { function(PDFLayout) {
/** /**
* Generates a content provider for polls * Generates a content provider for polls
* @constructor * @constructor
@ -211,9 +290,9 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
text: title, text: title,
style: 'description' style: 'description'
}, },
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("Yes")), PDFLayout.createBallotEntry(gettextCatalog.getString("Yes")),
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("No")), PDFLayout.createBallotEntry(gettextCatalog.getString("No")),
PdfPredefinedFunctions.createBallotEntry(gettextCatalog.getString("Abstain")), PDFLayout.createBallotEntry(gettextCatalog.getString("Abstain")),
], ],
margin: [0, 0, 0, sheetend] margin: [0, 0, 0, sheetend]
}; };
@ -237,7 +316,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
[createSection(), createSection()] [createSection(), createSection()]
], ],
}, },
layout: PdfPredefinedFunctions.getBallotLayoutLines() layout: PDFLayout.getBallotLayoutLines()
}]; }];
}; };
@ -252,20 +331,20 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
.factory('MotionCatalogContentProvider', [ .factory('MotionCatalogContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
'Category',
'Config', 'Config',
function(gettextCatalog, PdfPredefinedFunctions, Config) { function(gettextCatalog, PDFLayout, Category, Config) {
/** /**
* Constructor * Constructor
* @function * @function
* @param {object} allMotions - A sorted array of all motions to parse * @param {object} allMotions - A sorted array of all motions to parse
* @param {object} $scope - Current $scope * @param {object} $scope - Current $scope
* @param {object} User - Current user
*/ */
var createInstance = function(allMotions, $scope, User, Category) { var createInstance = function(allMotions, $scope) {
var title = PdfPredefinedFunctions.createTitle( var title = PDFLayout.createTitle(
gettextCatalog.getString(Config.get('motions_export_title').value) gettextCatalog.getString(Config.get('motions_export_title').value)
); );
@ -284,7 +363,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
var createTOContent = function() { var createTOContent = function() {
var heading = { var heading = {
text: gettextCatalog.getString("Table of contents"), text: gettextCatalog.getString("Table of contents"),
style: "heading" style: "heading2"
}; };
var toc = []; var toc = [];
@ -310,7 +389,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
return [ return [
heading, heading,
toc, toc,
PdfPredefinedFunctions.addPageBreak() PDFLayout.addPageBreak()
]; ];
}; };
@ -319,7 +398,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
if (Category.getAll().length > 0) { if (Category.getAll().length > 0) {
var heading = { var heading = {
text: gettextCatalog.getString("Categories"), text: gettextCatalog.getString("Categories"),
style: "heading" style: "heading2"
}; };
var toc = []; var toc = [];
@ -344,7 +423,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
return [ return [
heading, heading,
toc, toc,
PdfPredefinedFunctions.addPageBreak() PDFLayout.addPageBreak()
]; ];
} else { } else {
// if there are no categories, return "empty string" // if there are no categories, return "empty string"
@ -359,7 +438,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
angular.forEach(allMotions, function(motion, key) { angular.forEach(allMotions, function(motion, key) {
motionContent.push(motion.getContent()); motionContent.push(motion.getContent());
if (key < allMotions.length - 1) { if (key < allMotions.length - 1) {
motionContent.push(PdfPredefinedFunctions.addPageBreak()); motionContent.push(PDFLayout.addPageBreak());
} }
}); });

View File

@ -926,7 +926,7 @@ angular.module('OpenSlidesApp.motions.site', [
//post-request to convert the images. Async. //post-request to convert the images. Async.
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) { $http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake); var converter = PdfMakeConverter.createInstance(data.images, pdfMake);
var motionContentProviderArray = []; var motionContentProviderArray = [];
//convert the filtered motions to motionContentProviders //convert the filtered motions to motionContentProviders
@ -934,7 +934,7 @@ angular.module('OpenSlidesApp.motions.site', [
motionContentProviderArray.push(MotionContentProvider.createInstance(converter, motion, $scope, User, $http)); motionContentProviderArray.push(MotionContentProvider.createInstance(converter, motion, $scope, User, $http));
}); });
var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, $scope, User, Category); var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, $scope, User, Category);
var documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider, data.defaultFont); var documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider);
pdfMake.createPdf(documentProvider.getDocument()).download(filename); pdfMake.createPdf(documentProvider.getDocument()).download(filename);
}); });
}; };

View File

@ -132,11 +132,13 @@
<!-- Recommendation --> <!-- Recommendation -->
<div ng-if="config('motions_recommendations_by') != ''"> <div ng-if="config('motions_recommendations_by') != ''">
<h3 ng-if="!motion.isAllowed('change_recommendation')" class="heading" translate>Recommendation</h3> <h3 ng-if="!motion.isAllowed('change_recommendation')" class="heading">
{{ config('motions_recommendations_by') }}
</h3>
<div ng-if="motion.isAllowed('change_recommendation')" class="heading"> <div ng-if="motion.isAllowed('change_recommendation')" class="heading">
<span uib-dropdown> <span uib-dropdown>
<a href id="recommendation-dropdown" class="drop-down-name" uib-dropdown-toggle> <a href id="recommendation-dropdown" class="drop-down-name" uib-dropdown-toggle>
<translate>Recommendation</translate> {{ config('motions_recommendations_by') }}
<i class="fa fa-cog"></i> <i class="fa fa-cog"></i>
</a> </a>
<ul uib-dropdown-menu class="dropdown-menu" aria-labelledby="recommendation-dropdown"> <ul uib-dropdown-menu class="dropdown-menu" aria-labelledby="recommendation-dropdown">

View File

@ -6,13 +6,13 @@ angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf'])
.factory('UserListContentProvider', [ .factory('UserListContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { function(gettextCatalog, PDFLayout) {
var createInstance = function(userList, groups) { var createInstance = function(userList, groups) {
//use the Predefined Functions to create the title //use the Predefined Functions to create the title
var title = PdfPredefinedFunctions.createTitle(gettextCatalog.getString("List of participants")); var title = PDFLayout.createTitle(gettextCatalog.getString("List of participants"));
//function to generate the user list //function to generate the user list
var createUserList = function() { var createUserList = function() {
@ -35,19 +35,19 @@ angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf'])
var userJsonObj = [ var userJsonObj = [
{ {
text: "" + (counter+1), text: "" + (counter+1),
style: PdfPredefinedFunctions.flipTableRowStyle(userJsonList.length) style: PDFLayout.flipTableRowStyle(userJsonList.length)
}, },
{ {
text: user.short_name, text: user.short_name,
style: PdfPredefinedFunctions.flipTableRowStyle(userJsonList.length) style: PDFLayout.flipTableRowStyle(userJsonList.length)
}, },
{ {
text: user.structure_level, text: user.structure_level,
style: PdfPredefinedFunctions.flipTableRowStyle(userJsonList.length) style: PDFLayout.flipTableRowStyle(userJsonList.length)
}, },
{ {
text: userGroups.join(" "), text: userGroups.join(" "),
style: PdfPredefinedFunctions.flipTableRowStyle(userJsonList.length) style: PDFLayout.flipTableRowStyle(userJsonList.length)
} }
]; ];
userJsonList.push(userJsonObj); userJsonList.push(userJsonObj);
@ -107,8 +107,8 @@ angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf'])
.factory('UserAccessDataListContentProvider', [ .factory('UserAccessDataListContentProvider', [
'gettextCatalog', 'gettextCatalog',
'PdfPredefinedFunctions', 'PDFLayout',
function(gettextCatalog, PdfPredefinedFunctions) { function(gettextCatalog, PDFLayout) {
var createInstance = function(userList, groups, Config) { var createInstance = function(userList, groups, Config) {