Download multiple pdfs in a zip archive.
This commit is contained in:
parent
6899d93cbf
commit
845a1683bb
@ -17,6 +17,7 @@ Motions:
|
|||||||
- Fixed empty motion comment field in motion update form [#3194].
|
- Fixed empty motion comment field in motion update form [#3194].
|
||||||
- Removed server side image to base64 transformation and
|
- Removed server side image to base64 transformation and
|
||||||
added local transformation [#2705]
|
added local transformation [#2705]
|
||||||
|
- Added support for export motions in a zip archive [#3189].
|
||||||
|
|
||||||
Core:
|
Core:
|
||||||
- No reload on logoff. OpenSlides is now a full single page
|
- No reload on logoff. OpenSlides is now a full single page
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"jquery.cookie": "~1.4.1",
|
"jquery.cookie": "~1.4.1",
|
||||||
"js-data": "~2.9.0",
|
"js-data": "~2.9.0",
|
||||||
"js-data-angular": "~3.2.1",
|
"js-data-angular": "~3.2.1",
|
||||||
|
"jszip": "~3.1.3",
|
||||||
"lodash": "~4.16.0",
|
"lodash": "~4.16.0",
|
||||||
"ng-dialog": "~0.6.4",
|
"ng-dialog": "~0.6.4",
|
||||||
"ng-file-upload": "~11.2.3",
|
"ng-file-upload": "~11.2.3",
|
||||||
|
@ -859,10 +859,11 @@ angular.module('OpenSlidesApp.core.pdf', [])
|
|||||||
|
|
||||||
.factory('PdfCreate', [
|
.factory('PdfCreate', [
|
||||||
'$timeout',
|
'$timeout',
|
||||||
|
'$q',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'FileSaver',
|
'FileSaver',
|
||||||
'Messaging',
|
'Messaging',
|
||||||
function ($timeout, gettextCatalog, FileSaver, Messaging) {
|
function ($timeout, $q, gettextCatalog, FileSaver, Messaging) {
|
||||||
var filenameMessageMap = {};
|
var filenameMessageMap = {};
|
||||||
var b64toBlob = function(b64Data) {
|
var b64toBlob = function(b64Data) {
|
||||||
var byteCharacters = atob(b64Data);
|
var byteCharacters = atob(b64Data);
|
||||||
@ -898,19 +899,28 @@ angular.module('OpenSlidesApp.core.pdf', [])
|
|||||||
}, 1);
|
}, 1);
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
|
getBase64FromDocument: function (pdfDocument) {
|
||||||
|
return $q(function (resolve, reject) {
|
||||||
|
var pdfWorker = new Worker('/static/js/workers/pdf-worker.js');
|
||||||
|
pdfWorker.addEventListener('message', function (event) {
|
||||||
|
resolve(event.data);
|
||||||
|
});
|
||||||
|
pdfWorker.addEventListener('error', function (event) {
|
||||||
|
reject(event);
|
||||||
|
});
|
||||||
|
pdfWorker.postMessage(JSON.stringify(pdfDocument));
|
||||||
|
});
|
||||||
|
},
|
||||||
download: function (pdfDocument, filename) {
|
download: function (pdfDocument, filename) {
|
||||||
stateChange('info', filename);
|
stateChange('info', filename);
|
||||||
var pdfWorker = new Worker('/static/js/workers/pdf-worker.js');
|
|
||||||
|
|
||||||
pdfWorker.addEventListener('message', function (event) {
|
this.getBase64FromDocument(pdfDocument).then(function (data) {
|
||||||
var blob = b64toBlob(event.data);
|
var blob = b64toBlob(data);
|
||||||
stateChange('success', filename);
|
stateChange('success', filename);
|
||||||
FileSaver.saveAs(blob, filename);
|
FileSaver.saveAs(blob, filename);
|
||||||
|
}, function (error) {
|
||||||
|
stateChange('error', filename, error.message);
|
||||||
});
|
});
|
||||||
pdfWorker.addEventListener('error', function (event) {
|
|
||||||
stateChange('error', filename, event.message);
|
|
||||||
});
|
|
||||||
pdfWorker.postMessage(JSON.stringify(pdfDocument));
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,6 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|||||||
for (var i = 0; i < fields.length; i++) {
|
for (var i = 0; i < fields.length; i++) {
|
||||||
if (motion.comments[i] && canSeeComment(i)) {
|
if (motion.comments[i] && canSeeComment(i)) {
|
||||||
var title = gettextCatalog.getString('Comment') + ' ' + fields[i].name;
|
var title = gettextCatalog.getString('Comment') + ' ' + fields[i].name;
|
||||||
console.log(fields[i]);
|
|
||||||
if (!fields[i].public) {
|
if (!fields[i].public) {
|
||||||
title += ' (' + gettextCatalog.getString('private') + ')';
|
title += ' (' + gettextCatalog.getString('private') + ')';
|
||||||
}
|
}
|
||||||
@ -602,6 +601,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|||||||
|
|
||||||
.factory('MotionPdfExport', [
|
.factory('MotionPdfExport', [
|
||||||
'$http',
|
'$http',
|
||||||
|
'$q',
|
||||||
'Config',
|
'Config',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'MotionChangeRecommendation',
|
'MotionChangeRecommendation',
|
||||||
@ -614,15 +614,14 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|||||||
'PdfMakeBallotPaperProvider',
|
'PdfMakeBallotPaperProvider',
|
||||||
'PdfCreate',
|
'PdfCreate',
|
||||||
'PDFLayout',
|
'PDFLayout',
|
||||||
'$q',
|
'Messaging',
|
||||||
function ($http, Config, gettextCatalog, MotionChangeRecommendation, HTMLValidizer, PdfMakeConverter,
|
'FileSaver',
|
||||||
|
function ($http, $q, Config, gettextCatalog, MotionChangeRecommendation, HTMLValidizer, PdfMakeConverter,
|
||||||
MotionContentProvider, MotionCatalogContentProvider, PdfMakeDocumentProvider, PollContentProvider,
|
MotionContentProvider, MotionCatalogContentProvider, PdfMakeDocumentProvider, PollContentProvider,
|
||||||
PdfMakeBallotPaperProvider, PdfCreate, PDFLayout, $q) {
|
PdfMakeBallotPaperProvider, PdfCreate, PDFLayout, Messaging, FileSaver) {
|
||||||
return {
|
return {
|
||||||
export: function (motions, params, singleMotion) {
|
getDocumentProvider: function (motions, params, singleMotion) {
|
||||||
if (!params) {
|
params = _.clone(params || {}); // Clone this to avoid sideeffects.
|
||||||
params = {};
|
|
||||||
}
|
|
||||||
_.defaults(params, {
|
_.defaults(params, {
|
||||||
filename: gettextCatalog.getString('motions') + '.pdf',
|
filename: gettextCatalog.getString('motions') + '.pdf',
|
||||||
changeRecommendationMode: Config.get('motions_recommendation_text_mode').value,
|
changeRecommendationMode: Config.get('motions_recommendation_text_mode').value,
|
||||||
@ -643,11 +642,11 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|||||||
angular.forEach(motions, function (motion) {
|
angular.forEach(motions, function (motion) {
|
||||||
if (singleMotion) {
|
if (singleMotion) {
|
||||||
motion.changeRecommendations = MotionChangeRecommendation.filter({
|
motion.changeRecommendations = MotionChangeRecommendation.filter({
|
||||||
'where': {'motion_version_id': {'==': motion.active_version}}
|
'where': {'motion_version_id': {'==': params.version}}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
motion.changeRecommendations = MotionChangeRecommendation.filter({
|
motion.changeRecommendations = MotionChangeRecommendation.filter({
|
||||||
'where': {'motion_version_id': {'==': params.version}}
|
'where': {'motion_version_id': {'==': motion.active_version}}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var text = motion.getTextByMode(params.changeRecommendationMode, null);
|
var text = motion.getTextByMode(params.changeRecommendationMode, null);
|
||||||
@ -659,43 +658,93 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
|
|||||||
image_sources = image_sources.concat(tmp_image_sources);
|
image_sources = image_sources.concat(tmp_image_sources);
|
||||||
});
|
});
|
||||||
|
|
||||||
var image_map = {};
|
var imageMap = {};
|
||||||
_.forEach(image_sources, function (image_source) {
|
var imagePromises = _.map(image_sources, function (image_source) {
|
||||||
image_map[image_source] = PDFLayout.imageURLtoBase64(image_source);
|
return PDFLayout.imageURLtoBase64(image_source).then(function (base64Str) {
|
||||||
|
imageMap[image_source] = base64Str;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var image_promises = Object.keys(image_map).map(function( key ) {
|
return $q(function (resolve) {
|
||||||
return image_map[key];
|
//resolve promises to get base64
|
||||||
|
$q.all(imagePromises).then(function(base64Str) {
|
||||||
|
var converter = PdfMakeConverter.createInstance(imageMap);
|
||||||
|
var motionContentProviderArray = [];
|
||||||
|
|
||||||
|
//convert all motions to motionContentProviders
|
||||||
|
angular.forEach(motions, function (motion) {
|
||||||
|
var version = (singleMotion ? params.version : motion.active_version);
|
||||||
|
motionContentProviderArray.push(MotionContentProvider.createInstance(
|
||||||
|
converter, motion, version, params.changeRecommendationMode,
|
||||||
|
motion.changeRecommendations, params.lineNumberMode,
|
||||||
|
params.includeReason, params.includeComments
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
var documentProvider;
|
||||||
|
if (singleMotion) {
|
||||||
|
documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProviderArray[0]);
|
||||||
|
} else {
|
||||||
|
var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray);
|
||||||
|
documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(documentProvider);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
//resolv promises to get base64
|
export: function (motions, params, singleMotion) {
|
||||||
$q.all(image_promises).then(function(base64Str) {
|
_.defaults(params, {
|
||||||
Object.keys(image_map).map(function(key, i) {
|
filename: gettextCatalog.getString('motions') + '.pdf',
|
||||||
image_map[key] = base64Str[i];
|
});
|
||||||
});
|
this.getDocumentProvider(motions, params, singleMotion).then(
|
||||||
|
function (documentProvider) {
|
||||||
var converter = PdfMakeConverter.createInstance(image_map);
|
PdfCreate.download(documentProvider.getDocument(), params.filename);
|
||||||
var motionContentProviderArray = [];
|
|
||||||
|
|
||||||
//convert all motions to motionContentProviders
|
|
||||||
angular.forEach(motions, function (motion) {
|
|
||||||
var version = (singleMotion ? params.version : motion.active_version);
|
|
||||||
motionContentProviderArray.push(MotionContentProvider.createInstance(
|
|
||||||
converter, motion, version, params.changeRecommendationMode,
|
|
||||||
motion.changeRecommendations, params.lineNumberMode,
|
|
||||||
params.includeReason, params.includeComments
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
var documentProvider;
|
|
||||||
if (singleMotion) {
|
|
||||||
documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProviderArray[0]);
|
|
||||||
} else {
|
|
||||||
var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray);
|
|
||||||
documentProvider = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider);
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
exportZip: function (motions, params) {
|
||||||
|
var messageId = Messaging.addMessage('<i class="fa fa-spinner fa-pulse fa-lg spacer-right"></i>' +
|
||||||
|
gettextCatalog.getString('Generating PDFs and ZIP archive') + ' ...', 'info');
|
||||||
|
var zipFilename = params.filename || gettextCatalog.getString('motions') + '.zip';
|
||||||
|
params.filename = void 0; // clear this, so we do not override the default filenames for each pdf.
|
||||||
|
|
||||||
PdfCreate.download(documentProvider.getDocument(), params.filename);
|
var self = this;
|
||||||
|
var pdfs = {};
|
||||||
|
var pdfPromises = _.map(motions, function (motion) {
|
||||||
|
var identifier = motion.identifier ? '-' + motion.identifier : '';
|
||||||
|
var filename = gettextCatalog.getString('Motion') + identifier + '.pdf';
|
||||||
|
|
||||||
|
return $q(function (resolve, reject) {
|
||||||
|
// get documentProvider for every motion.
|
||||||
|
self.getDocumentProvider(motion, params, true).then(function (documentProvider) {
|
||||||
|
var doc = documentProvider.getDocument();
|
||||||
|
|
||||||
|
PdfCreate.getBase64FromDocument(doc).then(function (data) {
|
||||||
|
pdfs[filename] = data;
|
||||||
|
resolve();
|
||||||
|
}, function (error) {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for all documents to be generated. Then put them into a zip and download it.
|
||||||
|
$q.all(pdfPromises).then(function () {
|
||||||
|
var zip = new JSZip();
|
||||||
|
_.forEach(pdfs, function (data, filename) {
|
||||||
|
zip.file(filename, data, {base64: true});
|
||||||
|
});
|
||||||
|
Messaging.createOrEditMessage(messageId, '<i class="fa fa-check fa-lg spacer-right"></i>' +
|
||||||
|
gettextCatalog.getString('ZIP successfully generated.'), 'success', {timeout: 3000});
|
||||||
|
zip.generateAsync({type: 'blob'}).then(function (content) {
|
||||||
|
FileSaver.saveAs(content, zipFilename);
|
||||||
|
});
|
||||||
|
}, function (error) {
|
||||||
|
Messaging.createOrEditMessage(messageId, '<i class="fa fa-exclamation-triangle fa-lg ' +
|
||||||
|
'spacer-right"></i>' + gettextCatalog.getString('Error while generating ZIP file') +
|
||||||
|
': <code>' + error + '</code>', 'error');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
createPollPdf: function (motion, version) {
|
createPollPdf: function (motion, version) {
|
||||||
|
@ -733,9 +733,23 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
hideExpression: "model.format !== 'pdf'",
|
hideExpression: "model.format !== 'pdf'",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!singleMotion) {
|
||||||
|
fields.push({
|
||||||
|
key: 'pdfFormat',
|
||||||
|
type: 'select-radio',
|
||||||
|
templateOptions: {
|
||||||
|
label: gettextCatalog.getString('PDF format'),
|
||||||
|
options: [
|
||||||
|
{name: gettextCatalog.getString('One PDF'), value: 'pdf'},
|
||||||
|
{name: gettextCatalog.getString('Multiple PDFs in a zip arcive'), value: 'zip'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
hideExpression: "model.format !== 'pdf'",
|
||||||
|
});
|
||||||
|
}
|
||||||
return fields;
|
return fields;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -761,6 +775,7 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
$scope.params = params || {};
|
$scope.params = params || {};
|
||||||
_.defaults($scope.params, {
|
_.defaults($scope.params, {
|
||||||
format: 'pdf',
|
format: 'pdf',
|
||||||
|
pdfFormat: 'pdf',
|
||||||
changeRecommendationMode: Config.get('motions_recommendation_text_mode').value,
|
changeRecommendationMode: Config.get('motions_recommendation_text_mode').value,
|
||||||
lineNumberMode: Config.get('motions_default_line_numbering').value,
|
lineNumberMode: Config.get('motions_default_line_numbering').value,
|
||||||
includeReason: true,
|
includeReason: true,
|
||||||
@ -772,7 +787,11 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
$scope.export = function () {
|
$scope.export = function () {
|
||||||
switch ($scope.params.format) {
|
switch ($scope.params.format) {
|
||||||
case 'pdf':
|
case 'pdf':
|
||||||
MotionPdfExport.export(motions, $scope.params, singleMotion);
|
if ($scope.params.pdfFormat === 'pdf') {
|
||||||
|
MotionPdfExport.export(motions, $scope.params, singleMotion);
|
||||||
|
} else {
|
||||||
|
MotionPdfExport.exportZip(motions, $scope.params);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'csv':
|
case 'csv':
|
||||||
MotionCsvExport.export(motions, $scope.params);
|
MotionCsvExport.export(motions, $scope.params);
|
||||||
|
Loading…
Reference in New Issue
Block a user