Add motion catalog over pdfmake (fixes #2299)

This commit is contained in:
Sean Engelhardt 2016-09-20 16:29:42 +02:00 committed by sean
parent fb2593b5a7
commit d4c4f944fe
3 changed files with 367 additions and 243 deletions

View File

@ -89,6 +89,22 @@ angular.module('OpenSlidesApp.core.site', [
header: header, header: header,
footer: footer, footer: footer,
content: content, content: content,
styles: {
title: {
fontSize: 30,
margin: [0,0,0,20],
bold: true
},
heading: {
fontSize: 16,
margin: [0,0,0,10],
bold: true
},
tableofcontent: {
fontSize: 12,
margin: [0,3]
}
}
}; };
}; };
return { return {

View File

@ -9,14 +9,11 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
* 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) {
/** var createInstance = function(converter, motion, $scope, User) {
* Text of motion
* @function // generates the text of the motion. Also septerates between line-numbers
* @param {object} motion - Current motion var textContent = function() {
* @param {object} $scope - Current $scope
*/
var textContent = function(motion, $scope) {
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.
@ -28,36 +25,23 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
} else { } else {
return converter.convertHTML(motion.getText($scope.version), $scope); return converter.convertHTML(motion.getText($scope.version), $scope);
} }
}, };
/**
* Generate text of reason // Generate text of reason
* @function var reasonContent = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
*/
reasonContent = function(motion, $scope) {
return converter.convertHTML(motion.getReason($scope.version), $scope); return converter.convertHTML(motion.getReason($scope.version), $scope);
}, };
/**
* Generate header text of motion // Generate header text of motion
* @function var motionHeader = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
*/
motionHeader = function(motion, $scope) {
var header = converter.createElement("text", gettextCatalog.getString("Motion") + " " + motion.identifier + ": " + motion.getTitle($scope.version)); var header = converter.createElement("text", gettextCatalog.getString("Motion") + " " + motion.identifier + ": " + motion.getTitle($scope.version));
header.bold = true; header.bold = true;
header.fontSize = 26; header.fontSize = 26;
return header; return header;
}, };
/**
* Generate text of signment // Generate text of signment
* @function var signment = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
* @param {object} User - Current user
*/
signment = function(motion, $scope, User) {
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:'); var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name)); var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name));
state.width = "70%"; state.width = "70%";
@ -67,14 +51,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
signment.margin = [10, 20, 0, 10]; signment.margin = [10, 20, 0, 10];
signment.lineHeight = 2.5; signment.lineHeight = 2.5;
return signment; return signment;
}, };
/**
* Generates polls // Generates polls
* @function var polls = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
*/
polls = function(motion, $scope) {
if (!motion.polls.length) return {}; if (!motion.polls.length) return {};
var pollLabel = converter.createElement("text", gettextCatalog.getString('Voting result') + ":"), var pollLabel = converter.createElement("text", gettextCatalog.getString('Voting result') + ":"),
results = function() { results = function() {
@ -85,7 +65,8 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
no = poll.no ? poll.no : '-', no = poll.no ? poll.no : '-',
noRelative = poll.getVote(poll.no, 'no').percentStr, noRelative = poll.getVote(poll.no, 'no').percentStr,
abstain = poll.abstain ? poll.abstain : '-', abstain = poll.abstain ? poll.abstain : '-',
abstainRelative = poll.getVote(poll.abstain, 'abstain').percentStr, abstainrelativeGet = poll.getVote(poll.abstain, 'abstain').percentStr,
abstainRelative = abstainrelativeGet ? abstainrelativeGet : '',
valid = poll.votesvalid ? poll.votesvalid : '-', valid = poll.votesvalid ? poll.votesvalid : '-',
validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr, validRelative = poll.getVote(poll.votesvalid, 'votesvalid').percentStr,
number = { number = {
@ -121,7 +102,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)), yesPart = converter.createElement("columns", createPart(gettextCatalog.getString("Yes"), yes, yesRelative)),
noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)), noPart = converter.createElement("columns", createPart(gettextCatalog.getString("No"), no, noRelative)),
abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)), abstainPart = converter.createElement("columns", createPart(gettextCatalog.getString("Abstain"), abstain, abstainRelative)),
totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid ballots"), valid, validRelative)), totalPart = converter.createElement("columns", createPart(gettextCatalog.getString("Valid votes"), valid, validRelative)),
heading = converter.createElement("columns", [number, headerText]), heading = converter.createElement("columns", [number, headerText]),
pollResult = converter.createElement("stack", [ pollResult = converter.createElement("stack", [
heading, yesPart, noPart, abstainPart, totalPart heading, yesPart, noPart, abstainPart, totalPart
@ -136,96 +117,74 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
result.margin = [10, 0, 0, 10]; result.margin = [10, 0, 0, 10];
result.lineHeight = 1; result.lineHeight = 1;
return result; return result;
}, };
/**
* Generates title section for motion // Generates title section for motion
* @function var titleSection = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
*/
titleSection = function(motion, $scope) {
var title = converter.createElement("text", motion.getTitle($scope.version)); var title = converter.createElement("text", motion.getTitle($scope.version));
title.bold = true; title.bold = true;
title.fontSize = 14; title.fontSize = 14;
title.margin = [0, 0, 0, 10]; title.margin = [0, 0, 0, 10];
return title; return title;
}, };
/**
* Generates reason section for polls // Generates reason section for polls
* @function var reason = function() {
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
*/
reason = function(motion, $scope) {
var r = converter.createElement("text", gettextCatalog.getString("Reason") + ":"); var r = converter.createElement("text", gettextCatalog.getString("Reason") + ":");
r.bold = true; r.bold = true;
r.fontSize = 14; r.fontSize = 14;
r.margin = [0, 30, 0, 10]; r.margin = [0, 30, 0, 10];
return r; return r;
}, };
/**
* Generates content as a pdfmake consumable //getters
* @function var getTitle = function() {
* @param {object} motion - Current motion return motion.getTitle($scope.verion);
* @param {object} $scope - Current $scope };
* @param {object} User - Current user
*/ var getIdentifier = function() {
getContent = function(motion, $scope, User) { return motion.identifier;
if (reasonContent(motion, $scope).length === 0 ) { };
var getCategory = function() {
return motion.category;
};
// Generates content as a pdfmake consumable
var getContent = function() {
if (reasonContent().length === 0 ) {
return [ return [
motionHeader(motion, $scope), motionHeader(),
signment(motion, $scope, User), signment(),
polls(motion, $scope), polls(),
titleSection(motion, $scope), titleSection(),
textContent(motion, $scope), textContent(),
]; ];
} else { } else {
return [ return [
motionHeader(motion, $scope), motionHeader(),
signment(motion, $scope, User), signment(),
polls(motion, $scope), polls(),
titleSection(motion, $scope), titleSection(),
textContent(motion, $scope), textContent(),
reason(motion, $scope), reason(),
reasonContent(motion, $scope) reasonContent()
]; ];
} }
}; };
return { return {
getContent: getContent getContent: getContent,
getTitle: getTitle,
getIdentifier: getIdentifier,
getCategory: getCategory
}; };
}; };
return { return {
createInstance: createInstance createInstance: createInstance
}; };
}]) }])
.factory('SingleMotionContentProvider', function() {
/**
* Generates a content provider
* @constructor
* @param {object} motionContentProvider - Generates pdfMake structure from motion
* @param {object} $scope - Current $scope
* @param {object} User - Current User
*/
var createInstance = function(motionContentProvider, motion, $scope, User) {
/**
* Returns Content for single motion
* @function
* @param {object} motion - Current motion
* @param {object} $scope - Current $scope
* @param {object} User - Current User
*/
var getContent = function() {
return motionContentProvider.getContent(motion, $scope, User);
};
return {
getContent: getContent
};
};
return {
createInstance: createInstance
};
})
.factory('PollContentProvider', function() { .factory('PollContentProvider', function() {
/** /**
* Generates a content provider for polls * Generates a content provider for polls
@ -356,6 +315,127 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
}; };
}) })
.factory('MotionCatalogContentProvider', ['gettextCatalog', function(gettextCatalog) {
/**
* Constructor
* @function
* @param {object} allMotions - A sorted array of all motions to parse
* @param {object} $scope - Current $scope
* @param {object} User - Current user
*/
var createInstance = function(allMotions, $scope, User, Category) {
//function to create the Table of contents
var createTitle = function() {
return {
text: gettextCatalog.getString("Motions"),
style: "title"
};
};
var createTOContent = function(motionTitles) {
var heading = {
text: gettextCatalog.getString("Table of contents"),
style: "heading",
};
var toc = [];
angular.forEach(motionTitles, function(title) {
toc.push({
text: gettextCatalog.getString("Motion") + " " + title,
style: "tableofcontent",
});
});
return [
heading,
toc,
addPageBreak()
];
};
// function to create the table of catergories (if any)
var createTOCatergories = function() {
if (Category.getAll().length > 0) {
var heading = {
text: gettextCatalog.getString("Categories"),
style: "heading"
};
var toc = [];
angular.forEach(Category.getAll(), function(cat) {
toc.push(
{
columns: [
{
text: cat.prefix,
style: 'tableofcontent',
width: 30
},
{
text: cat.name,
style: 'tableofcontent'
}
]
}
);
});
return [
heading,
toc,
addPageBreak()
];
} else {
// if there are no categories, return "empty string"
// pdfmake takes "null" literally and throws an error
return "";
}
};
// function to apply a pagebreak-keyword
var addPageBreak = function() {
return [
{
text: '',
pageBreak: 'after'
}
];
};
// returns the pure content of the motion, parseable by makepdf
var getContent = function() {
var motionContent = [];
var motionTitles = [];
var motionCategories = [];
angular.forEach(allMotions, function(motion, key) {
motionTitles.push(motion.getIdentifier() + ": " + motion.getTitle());
motionContent.push(motion.getContent());
if (key < allMotions.length - 1) {
motionContent.push(addPageBreak());
}
});
return [
createTitle(),
createTOCatergories(),
createTOContent(motionTitles),
motionContent
];
};
return {
getContent: getContent
};
};
return {
createInstance: createInstance
};
}])
.config([ .config([
'mainMenuProvider', 'mainMenuProvider',
'gettext', 'gettext',
@ -831,7 +911,14 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'User', 'User',
'Agenda', 'Agenda',
'MotionDocxExport', 'MotionDocxExport',
function($scope, $state, $http, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User, Agenda, MotionDocxExport) { 'MotionContentProvider',
'MotionCatalogContentProvider',
'PdfMakeConverter',
'PdfMakeDocumentProvider',
'gettextCatalog',
function($scope, $state, $http, ngDialog, MotionForm, Motion, Category, Tag, Workflow, User, Agenda, MotionDocxExport,
MotionContentProvider, MotionCatalogContentProvider, PdfMakeConverter,
PdfMakeDocumentProvider, gettextCatalog) {
Motion.bindAll({}, $scope, 'motions'); Motion.bindAll({}, $scope, 'motions');
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Tag.bindAll({}, $scope, 'tags'); Tag.bindAll({}, $scope, 'tags');
@ -866,7 +953,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
// add id // add id
$scope.multiselectFilter[filter].push(id); $scope.multiselectFilter[filter].push(id);
} }
} }
}; };
// function to sort by clicked column // function to sort by clicked column
@ -989,6 +1075,38 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
ngDialog.open(MotionForm.getDialog(motion)); ngDialog.open(MotionForm.getDialog(motion));
}; };
// Export as a pdf file
$scope.pdf_export = function() {
var filename = gettextCatalog.getString("Motions") + ".pdf";
var image_sources = [];
//save the arrays of the filtered motions to an array
angular.forEach($scope.motionsFiltered, function (motion) {
var content = motion.getText() + motion.getReason();
var map = Function.prototype.call.bind([].map);
var tmp_image_sources = map($(content).find("img"), function(element) {
return element.getAttribute("src");
});
image_sources = image_sources.concat(tmp_image_sources);
});
//post-request to convert the images. Async.
$http.post('/core/encode_media/', JSON.stringify(image_sources)).success(function(data) {
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake);
var motionContentProviderArray = [];
//convert the filtered motions to motionContentProviders
angular.forEach($scope.motionsFiltered, function (motion) {
motionContentProviderArray.push(MotionContentProvider.createInstance(converter, motion, $scope, User, $http));
});
var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, $scope, User, Category);
var documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider, data.defaultFont);
pdfMake.createPdf(documentProvider.getDocument()).download(filename);
});
};
// Export as a csv file // Export as a csv file
$scope.csv_export = function () { $scope.csv_export = function () {
var element = document.getElementById('downloadLinkCSV'); var element = document.getElementById('downloadLinkCSV');
@ -1065,7 +1183,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'Workflow', 'Workflow',
'Config', 'Config',
'motion', 'motion',
'SingleMotionContentProvider',
'MotionContentProvider', 'MotionContentProvider',
'PollContentProvider', 'PollContentProvider',
'PdfMakeConverter', 'PdfMakeConverter',
@ -1074,7 +1191,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'gettextCatalog', 'gettextCatalog',
'Projector', 'Projector',
function($scope, $http, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, Config, function($scope, $http, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, Config,
motion, SingleMotionContentProvider, MotionContentProvider, PollContentProvider, motion, MotionContentProvider, PollContentProvider,
PdfMakeConverter, PdfMakeDocumentProvider, MotionInlineEditing, gettextCatalog, Projector) { PdfMakeConverter, PdfMakeDocumentProvider, MotionInlineEditing, gettextCatalog, Projector) {
Motion.bindOne(motion.id, $scope, 'motion'); Motion.bindOne(motion.id, $scope, 'motion');
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
@ -1125,29 +1242,20 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
}; };
$scope.makePDF = function() { $scope.makePDF = function() {
var id = motion.identifier, var content = motion.getText() + motion.getReason();
slice = Function.prototype.call.bind([].slice), var map = Function.prototype.call.bind([].map);
map = Function.prototype.call.bind([].map), var image_sources = map($(content).find("img"), function(element) {
image_sources = map($(content).find("img"), function(element) {
return element.getAttribute("src"); return element.getAttribute("src");
}); });
$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);
* Converter for use with pdfMake var motionContentProvider = MotionContentProvider.createInstance(converter, motion, $scope, User, $http);
* @constructor var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider, data.defaultFont);
* @param {object} images - An object to resolve the BASE64 encoded images { "$src":$BASE64DATA } var filename = gettextCatalog.getString("Motion") + "-" + motion.identifier + ".pdf";
* @param {object} fonts - An object representing the available custom fonts
* @param {object} pdfMake - pdfMake object for enhancement with custom fonts
*/
var converter = PdfMakeConverter.createInstance(data.images, data.fonts, pdfMake),
motionContentProvider = MotionContentProvider.createInstance(converter),
contentProvider = SingleMotionContentProvider.createInstance(motionContentProvider, motion, $scope, User),
documentProvider = PdfMakeDocumentProvider.createInstance(contentProvider, data.defaultFont),
filename = gettextCatalog.getString("Motion") + "-" + id + ".pdf";
pdfMake.createPdf(documentProvider.getDocument()).download(filename); pdfMake.createPdf(documentProvider.getDocument()).download(filename);
}); });
}; };
//make PDF for polls //make PDF for polls

View File

@ -47,7 +47,7 @@
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport"> <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownExport">
<!-- PDF export --> <!-- PDF export -->
<li> <li>
<a ui-sref="motions_pdf" target="_blank"> <a href="" ng-click="pdf_export()">
<i class="fa fa-file-pdf-o fa-lg"></i> <i class="fa fa-file-pdf-o fa-lg"></i>
<translate>PDF</translate> <translate>PDF</translate>
</a> </a>