572 lines
20 KiB
JavaScript
572 lines
20 KiB
JavaScript
(function () {
|
|
|
|
"use strict";
|
|
|
|
angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|
|
|
.factory('MotionContentProvider', [
|
|
'gettextCatalog',
|
|
'PDFLayout',
|
|
'Category',
|
|
'Config',
|
|
'Motion',
|
|
function(gettextCatalog, PDFLayout, Category, Config, Motion) {
|
|
/**
|
|
* Provides the content as JS objects for Motions in pdfMake context
|
|
* @constructor
|
|
*/
|
|
|
|
var createInstance = function(converter, motion, $scope) {
|
|
|
|
// title
|
|
var identifier = motion.identifier ? ' ' + motion.identifier : '';
|
|
var title = PDFLayout.createTitle(
|
|
gettextCatalog.getString('Motion') + identifier + ': ' +
|
|
motion.getTitle($scope.version)
|
|
);
|
|
|
|
// subtitle
|
|
var subtitleLines = [];
|
|
if (motion.parent_id) {
|
|
var parentMotion = Motion.get(motion.parent_id);
|
|
subtitleLines.push(
|
|
gettextCatalog.getString('Amendment of motion') + ': ' +
|
|
(parentMotion.identifier ? parentMotion.identifier : parentMotion.getTitle())
|
|
);
|
|
}
|
|
subtitleLines.push(gettextCatalog.getString('Sequential number') + ': ' + motion.id);
|
|
var subtitle = PDFLayout.createSubtitle(subtitleLines);
|
|
|
|
// 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
|
|
if (motion.polls.length > 0 && motion.polls[0].has_votes) {
|
|
var column1 = [];
|
|
var column2 = [];
|
|
var column3 = [];
|
|
motion.polls.map(function(poll, index) {
|
|
if (poll.has_votes) {
|
|
// votenumber
|
|
if (motion.polls.length > 1) {
|
|
column1.push(index + 1 + '. ' + gettextCatalog.getString('Vote'));
|
|
column2.push('');
|
|
column3.push('');
|
|
}
|
|
// 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'
|
|
}
|
|
]);
|
|
}
|
|
|
|
// summary of change recommendations (for motion diff version only)
|
|
if ($scope.viewChangeRecommendations) {
|
|
if ($scope.viewChangeRecommendations.mode == "diff") {
|
|
var columnLineNumbers = [];
|
|
var columnChangeType = [];
|
|
angular.forEach(_.orderBy($scope.change_recommendations, ['line_from']), function(change) {
|
|
// line numbers column
|
|
var line;
|
|
if (change.line_from >= change.line_to - 1) {
|
|
line = change.line_from;
|
|
} else {
|
|
line = change.line_from + ' - ' + (change.line_to - 1);
|
|
}
|
|
columnLineNumbers.push(
|
|
gettextCatalog.getString('Line') + ' ' + line + ': '
|
|
);
|
|
// change type column
|
|
if (change.getType(motion.getVersion($scope.version).text) === 0) {
|
|
columnChangeType.push(gettextCatalog.getString("Replacement"));
|
|
} else if (change.getType(motion.getVersion($scope.version).text) === 1) {
|
|
columnChangeType.push(gettextCatalog.getString("Insertion"));
|
|
} else if (change.getType(motion.getVersion($scope.version).text) === 2) {
|
|
columnChangeType.push(gettextCatalog.getString("Deletion"));
|
|
}
|
|
});
|
|
metaTableBody.push([
|
|
{
|
|
text: gettextCatalog.getString('Summary of change recommendations'),
|
|
style: ['bold', 'grey']
|
|
},
|
|
{
|
|
columns: [
|
|
{
|
|
text: columnLineNumbers.join('\n'),
|
|
width: 'auto'
|
|
},
|
|
{
|
|
text: columnChangeType.join('\n'),
|
|
width: 'auto'
|
|
}
|
|
],
|
|
columnGap: 7,
|
|
style: 'grey'
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
|
|
// build table
|
|
// Used placeholder for 'layout' functions whiche are
|
|
// replaced by lineWitdh/lineColor function in pfd-worker.js.
|
|
// TODO: Remove placeholder and us static values for LineWidth and LineColor
|
|
// if pdfmake has fixed this.
|
|
var metaTableJsonString = {
|
|
table: {
|
|
widths: ['30%','70%'],
|
|
body: metaTableBody,
|
|
},
|
|
margin: [0, 0, 0, 20],
|
|
layout: '{{motion-placeholder-to-insert-functions-here}}'
|
|
};
|
|
return metaTableJsonString;
|
|
};
|
|
|
|
// motion title
|
|
var motionTitle = function() {
|
|
return [{
|
|
text: motion.getTitle($scope.version),
|
|
style: 'heading3'
|
|
}];
|
|
};
|
|
|
|
// motion preamble
|
|
var motionPreamble = function () {
|
|
return {
|
|
text: Config.translate(Config.get('motions_preamble').value),
|
|
margin: [0, 10, 0, 0]
|
|
};
|
|
};
|
|
|
|
|
|
// motion text (with line-numbers)
|
|
var motionText = function() {
|
|
var motionTextContent = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version);
|
|
return converter.convertHTML(motionTextContent, $scope.lineNumberMode);
|
|
};
|
|
|
|
// motion reason heading
|
|
var motionReason = function() {
|
|
var reason = [];
|
|
if (motion.getReason($scope.version)) {
|
|
reason.push({
|
|
text: gettextCatalog.getString('Reason'),
|
|
style: 'heading3',
|
|
marginTop: 25,
|
|
});
|
|
reason.push(converter.convertHTML(motion.getReason($scope.version), $scope.lineNumberMode));
|
|
}
|
|
return reason;
|
|
};
|
|
|
|
|
|
// getters
|
|
var getTitle = function() {
|
|
return motion.getTitle($scope.version);
|
|
};
|
|
|
|
var getIdentifier = function() {
|
|
return motion.identifier ? motion.identifier : '';
|
|
};
|
|
|
|
var getCategory = function() {
|
|
return motion.category;
|
|
};
|
|
|
|
// Generates content as a pdfmake consumable
|
|
var getContent = function() {
|
|
var content = [
|
|
title,
|
|
subtitle,
|
|
metaTable(),
|
|
motionTitle(),
|
|
motionPreamble(),
|
|
motionText(),
|
|
];
|
|
if (motionReason()) {
|
|
content.push(motionReason());
|
|
}
|
|
return content;
|
|
};
|
|
return {
|
|
getContent: getContent,
|
|
getTitle: getTitle,
|
|
getIdentifier: getIdentifier,
|
|
getCategory: getCategory
|
|
};
|
|
};
|
|
|
|
return {
|
|
createInstance: createInstance
|
|
};
|
|
}])
|
|
|
|
.factory('PollContentProvider', [
|
|
'PDFLayout',
|
|
'Config',
|
|
'User',
|
|
function(PDFLayout, Config, User) {
|
|
/**
|
|
* Generates a content provider for polls
|
|
* @constructor
|
|
* @param {string} title - title of poll
|
|
* @param {string} id - if of poll
|
|
* @param {object} gettextCatalog - for translation
|
|
*/
|
|
var createInstance = function(title, id, gettextCatalog) {
|
|
|
|
/**
|
|
* Returns a single section on the ballot paper
|
|
* @function
|
|
*/
|
|
var createSection = function() {
|
|
var sheetend = 75;
|
|
return {
|
|
stack: [{
|
|
text: gettextCatalog.getString("Motion") + " " + id,
|
|
style: 'title',
|
|
}, {
|
|
text: title,
|
|
style: 'description'
|
|
},
|
|
PDFLayout.createBallotEntry(gettextCatalog.getString("Yes")),
|
|
PDFLayout.createBallotEntry(gettextCatalog.getString("No")),
|
|
PDFLayout.createBallotEntry(gettextCatalog.getString("Abstain")),
|
|
],
|
|
margin: [0, 0, 0, sheetend]
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Returns Content for single motion
|
|
* @function
|
|
* @param {string} id - if of poll
|
|
*/
|
|
var getContent = function() {
|
|
var content = [];
|
|
var amount;
|
|
var amount_method = Config.get('motions_pdf_ballot_papers_selection').value;
|
|
switch (amount_method) {
|
|
case 'NUMBER_OF_ALL_PARTICIPANTS':
|
|
amount = User.getAll().length;
|
|
break;
|
|
case 'NUMBER_OF_DELEGATES':
|
|
//TODO: assumption that DELEGATES is always group id 2. This may not be true
|
|
var group_id = 2;
|
|
amount = User.filter({where: {'groups_id': {contains:group_id} }}).length;
|
|
break;
|
|
case 'CUSTOM_NUMBER':
|
|
amount = Config.get('motions_pdf_ballot_papers_number').value;
|
|
break;
|
|
default:
|
|
// should not happen.
|
|
amount = 0;
|
|
}
|
|
var fullpages = Math.floor(amount / 8);
|
|
|
|
for (var i=0; i < fullpages; i++) {
|
|
content.push({
|
|
table: {
|
|
headerRows: 1,
|
|
widths: ['*', '*'],
|
|
body: [
|
|
[createSection(), createSection()],
|
|
[createSection(), createSection()],
|
|
[createSection(), createSection()],
|
|
[createSection(), createSection()]
|
|
],
|
|
pageBreak: 'after'
|
|
},
|
|
layout: PDFLayout.getBallotLayoutLines(),
|
|
rowsperpage: 4
|
|
});
|
|
}
|
|
amount = amount - (fullpages * 8);
|
|
if (amount > 0) {
|
|
var partialpagebody = [];
|
|
while (amount > 1) {
|
|
partialpagebody.push([createSection(), createSection()]);
|
|
amount -=2;
|
|
}
|
|
if (amount == 1) {
|
|
partialpagebody.push([createSection(), '']);
|
|
}
|
|
content.push({
|
|
table: {
|
|
headerRows: 1,
|
|
widths: ['50%', '50%'],
|
|
body: partialpagebody
|
|
},
|
|
layout: PDFLayout.getBallotLayoutLines(),
|
|
rowsperpage: 4
|
|
});
|
|
}
|
|
return content;
|
|
};
|
|
|
|
return {
|
|
getContent: getContent,
|
|
};
|
|
};
|
|
return {
|
|
createInstance: createInstance
|
|
};
|
|
}])
|
|
|
|
.factory('MotionCatalogContentProvider', [
|
|
'gettextCatalog',
|
|
'PDFLayout',
|
|
'Category',
|
|
'Config',
|
|
function(gettextCatalog, PDFLayout, Category, Config) {
|
|
|
|
/**
|
|
* Constructor
|
|
* @function
|
|
* @param {object} allMotions - A sorted array of all motions to parse
|
|
* @param {object} $scope - Current $scope
|
|
*/
|
|
var createInstance = function(allMotions, $scope) {
|
|
|
|
var title = PDFLayout.createTitle(
|
|
Config.translate(Config.get('motions_export_title').value)
|
|
);
|
|
|
|
var createPreamble = function() {
|
|
var preambleText = Config.get('motions_export_preamble').value;
|
|
if (preambleText) {
|
|
return {
|
|
text: preambleText,
|
|
style: "preamble"
|
|
};
|
|
} else {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
var createTOContent = function() {
|
|
var heading = {
|
|
text: gettextCatalog.getString("Table of contents"),
|
|
style: "heading2"
|
|
};
|
|
|
|
var toc = [];
|
|
angular.forEach(allMotions, function(motion) {
|
|
var identifier = motion.getIdentifier() ? motion.getIdentifier() : '';
|
|
toc.push(
|
|
{
|
|
columns: [
|
|
{
|
|
text: identifier,
|
|
style: 'tableofcontent',
|
|
width: 70
|
|
},
|
|
{
|
|
text: motion.getTitle(),
|
|
style: 'tableofcontent'
|
|
}
|
|
]
|
|
}
|
|
);
|
|
});
|
|
|
|
return [
|
|
heading,
|
|
toc,
|
|
PDFLayout.addPageBreak()
|
|
];
|
|
};
|
|
|
|
// function to create the table of catergories (if any)
|
|
var createTOCategories = function() {
|
|
var categories = [];
|
|
_.forEach(allMotions, function(motion) {
|
|
var category = motion.getCategory();
|
|
if (category) {
|
|
categories.push(category);
|
|
}
|
|
});
|
|
categories = _.uniqBy(categories, 'id');
|
|
if (categories.length > 1) {
|
|
var heading = {
|
|
text: gettextCatalog.getString("Categories"),
|
|
style: "heading2"
|
|
};
|
|
|
|
var toc = [];
|
|
angular.forEach(_.orderBy(categories, ['prefix']), function(cat) {
|
|
toc.push(
|
|
{
|
|
columns: [
|
|
{
|
|
text: cat.prefix,
|
|
style: 'tableofcontent',
|
|
width: 50
|
|
},
|
|
{
|
|
text: cat.name,
|
|
style: 'tableofcontent'
|
|
}
|
|
]
|
|
}
|
|
);
|
|
});
|
|
|
|
return [
|
|
heading,
|
|
toc,
|
|
PDFLayout.addPageBreak()
|
|
];
|
|
} else {
|
|
// if there are no categories, return "empty string"
|
|
// pdfmake takes "null" literally and throws an error
|
|
return "";
|
|
}
|
|
};
|
|
|
|
// returns the pure content of the motion, parseable by pdfmake
|
|
var getContent = function() {
|
|
var motionContent = [];
|
|
angular.forEach(allMotions, function(motion, key) {
|
|
motionContent.push(motion.getContent());
|
|
if (key < allMotions.length - 1) {
|
|
motionContent.push(PDFLayout.addPageBreak());
|
|
}
|
|
});
|
|
|
|
return [
|
|
title,
|
|
createPreamble(),
|
|
createTOCategories(),
|
|
createTOContent(),
|
|
motionContent
|
|
];
|
|
};
|
|
return {
|
|
getContent: getContent
|
|
};
|
|
};
|
|
|
|
return {
|
|
createInstance: createInstance
|
|
};
|
|
}]);
|
|
|
|
}());
|