From c68d3f0e4a872931cc3d4f89a4c08f67eb191b3e Mon Sep 17 00:00:00 2001 From: FinnStutzenstein Date: Fri, 31 Mar 2017 14:59:31 +0200 Subject: [PATCH] Export dialog for motions --- CHANGELOG | 4 +- openslides/core/static/css/app.css | 4 + openslides/core/static/js/core/site.js | 4 + .../static/templates/core/select-radio.html | 17 ++ openslides/motions/static/js/motions/csv.js | 30 +- openslides/motions/static/js/motions/docx.js | 42 ++- .../static/js/motions/motion-services.js | 54 ---- openslides/motions/static/js/motions/pdf.js | 265 +++++++++++++----- openslides/motions/static/js/motions/site.js | 264 ++++++++++++----- .../templates/motions/motion-detail.html | 4 +- .../templates/motions/motion-export-form.html | 14 + .../static/templates/motions/motion-list.html | 58 ++-- 12 files changed, 517 insertions(+), 243 deletions(-) create mode 100644 openslides/core/static/templates/core/select-radio.html create mode 100644 openslides/motions/static/templates/motions/motion-export-form.html diff --git a/CHANGELOG b/CHANGELOG index f775be81c..960a0a9d0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,9 +4,11 @@ https://openslides.org/ -Version 2.1.2 (unreleased) +Version 2.2 (unreleased) ========================== +Motions: +- New export dialog. Version 2.1.1 (2017-04-05) ========================== diff --git a/openslides/core/static/css/app.css b/openslides/core/static/css/app.css index befe406b0..ca8755442 100644 --- a/openslides/core/static/css/app.css +++ b/openslides/core/static/css/app.css @@ -1346,6 +1346,10 @@ img { padding: 0; } +.btn-group > label > input[type=radio] { + display: none; +} + /* user details */ .user_details fieldset { margin-bottom: 10px; diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js index 984eb51fe..8ef9bce61 100644 --- a/openslides/core/static/js/core/site.js +++ b/openslides/core/static/js/core/site.js @@ -648,6 +648,10 @@ angular.module('OpenSlidesApp.core.site', [ templateUrl: 'static/templates/core/checkbox.html', overwriteOk: true, }); + formlyConfig.setType({ + name: 'select-radio', + templateUrl: 'static/templates/core/select-radio.html', + }); formlyConfig.setType({ name: 'select-single', extends: 'select', diff --git a/openslides/core/static/templates/core/select-radio.html b/openslides/core/static/templates/core/select-radio.html new file mode 100644 index 000000000..a4c286c8a --- /dev/null +++ b/openslides/core/static/templates/core/select-radio.html @@ -0,0 +1,17 @@ +
+
+ +
+
+ +
+
diff --git a/openslides/motions/static/js/motions/csv.js b/openslides/motions/static/js/motions/csv.js index 1fdba56e2..feacfcf98 100644 --- a/openslides/motions/static/js/motions/csv.js +++ b/openslides/motions/static/js/motions/csv.js @@ -6,8 +6,10 @@ angular.module('OpenSlidesApp.motions.csv', []) .factory('MotionCsvExport', [ 'gettextCatalog', + 'Config', 'CsvDownload', - function (gettextCatalog, CsvDownload) { + 'lineNumberingService', + function (gettextCatalog, Config, CsvDownload, lineNumberingService) { var makeHeaderline = function () { var headerline = ['Identifier', 'Title', 'Text', 'Reason', 'Submitter', 'Category', 'Origin']; return _.map(headerline, function (entry) { @@ -15,16 +17,36 @@ angular.module('OpenSlidesApp.motions.csv', []) }); }; return { - export: function (motions) { + export: function (motions, params) { + if (!params) { + params = {}; + } + _.defaults(params, { + filename: 'motions-export.csv', + changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, + includeReason: true, + }); + if (!_.includes(['original', 'changed', 'agreed'], params.changeRecommendationMode)) { + params.changeRecommendationMode = 'original'; + } + var csvRows = [ makeHeaderline() ]; _.forEach(motions, function (motion) { + // TODO: Add a parameter 'noLineBreaks' to 'getTextByMode'. Currently linenumbers are + // removed directy after inserting --> not necessary. (See issue 3183) + var text = motion.getTextByMode(params.changeRecommendationMode); + text = lineNumberingService.stripLineNumbers(text); var row = []; row.push('"' + motion.identifier !== null ? motion.identifier : '' + '"'); row.push('"' + motion.getTitle() + '"'); - row.push('"' + motion.getText() + '"'); - row.push('"' + motion.getReason() + '"'); + row.push('"' + text + '"'); + if (params.includeReason) { + row.push('"' + motion.getReason() + '"'); + } else { + row.push('""'); + } var submitter = motion.submitters[0] ? motion.submitters[0].get_full_name() : ''; row.push('"' + submitter + '"'); var category = motion.category ? motion.category.name : ''; diff --git a/openslides/motions/static/js/motions/docx.js b/openslides/motions/static/js/motions/docx.js index 1065ca8a1..b09759578 100644 --- a/openslides/motions/static/js/motions/docx.js +++ b/openslides/motions/static/js/motions/docx.js @@ -8,9 +8,11 @@ angular.module('OpenSlidesApp.motions.docx', []) '$http', '$q', 'Config', + 'Category', 'gettextCatalog', 'FileSaver', - function ($http, $q, Config, gettextCatalog, FileSaver) { + 'lineNumberingService', + function ($http, $q, Config, Category, gettextCatalog, FileSaver, lineNumberingService) { var PAGEBREAK = ''; var TAGS_NO_PARAM = ['b', 'strong', 'em', 'i']; @@ -19,10 +21,9 @@ angular.module('OpenSlidesApp.motions.docx', []) var relationships; var contentTypes; - // $scope.motionsFiltered, $scope.categories - - var getData = function (motions, categories) { + var getData = function (motions, params) { var data = {}; + var categories = Category.getAll(); // header var headerline1 = [ Config.translate(Config.get('general_event_name').value), @@ -47,7 +48,7 @@ angular.module('OpenSlidesApp.motions.docx', []) // motions data.tableofcontents_translation = gettextCatalog.getString('Table of contents'); - data.motions = getMotionFullData(motions); + data.motions = getMotionFullData(motions, params); data.motions_list = getMotionShortData(motions); data.no_motions = gettextCatalog.getString('No motions available.'); @@ -72,13 +73,18 @@ angular.module('OpenSlidesApp.motions.docx', []) }); }; - var getMotionFullData = function (motions) { + var getMotionFullData = function (motions, params) { var translation = gettextCatalog.getString('Motion'), sequential_translation = gettextCatalog.getString('Sequential number'), submitters_translation = gettextCatalog.getString('Submitters'), status_translation = gettextCatalog.getString('Status'), reason_translation = gettextCatalog.getString('Reason'), data = _.map(motions, function (motion) { + // TODO: Add a parameter 'noLineBreaks' to 'getTextByMode'. Currently linenumbers are + // removed directy after inserting --> not necessary. (See issue #3183) + var text = motion.getTextByMode(params.changeRecommendationMode); + text = lineNumberingService.stripLineNumbers(text); + var reason = params.includeReason ? motion.getReason() : ''; return { motion_translation: translation, sequential_translation: sequential_translation, @@ -92,9 +98,9 @@ angular.module('OpenSlidesApp.motions.docx', []) status_translation: status_translation, status: motion.getStateName(), preamble: gettextCatalog.getString(Config.get('motions_preamble').value), - text: html2docx(motion.getText()), - reason_translation: motion.getReason().length === 0 ? '' : reason_translation, - reason: html2docx(motion.getReason()), + text: html2docx(text), + reason_translation: reason.length === 0 ? '' : reason_translation, + reason: html2docx(reason), pagebreak: PAGEBREAK, }; }); @@ -353,7 +359,19 @@ angular.module('OpenSlidesApp.motions.docx', []) }; return { - export: function (motions, categories) { + export: function (motions, params) { + if (!params) { + params = {}; + } + _.defaults(params, { + filename: 'motions-export.docx', + changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, + includeReason: true, + }); + if (!_.includes(['original', 'changed', 'agreed'], params.changeRecommendationMode)) { + params.changeRecommendationMode = 'original'; + } + images = []; relationships = []; contentTypes = []; @@ -361,7 +379,7 @@ angular.module('OpenSlidesApp.motions.docx', []) var content = window.atob(success.data); var doc = new Docxgen(content); - doc.setData(getData(motions, categories)); + doc.setData(getData(motions, params)); doc.render(); var zip = doc.getZip(); @@ -384,7 +402,7 @@ angular.module('OpenSlidesApp.motions.docx', []) // wait for all images to be resolved $q.all(imgPromises).then(function () { var out = zip.generate({type: 'blob'}); - FileSaver.saveAs(out, 'motions-export.docx'); + FileSaver.saveAs(out, params.filename); }); }); }, diff --git a/openslides/motions/static/js/motions/motion-services.js b/openslides/motions/static/js/motions/motion-services.js index b533dad92..cb9fd768e 100644 --- a/openslides/motions/static/js/motions/motion-services.js +++ b/openslides/motions/static/js/motions/motion-services.js @@ -4,60 +4,6 @@ angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', 'OpenSlidesApp.motions.lineNumbering']) -.factory('MotionPDFExport', [ - 'HTMLValidizer', - 'Motion', - 'User', - 'PdfMakeConverter', - 'PdfMakeDocumentProvider', - 'PdfMakeBallotPaperProvider', - 'MotionContentProvider', - 'PollContentProvider', - 'gettextCatalog', - '$http', - 'PdfCreate', - function (HTMLValidizer, Motion, User, PdfMakeConverter, PdfMakeDocumentProvider, PdfMakeBallotPaperProvider, - MotionContentProvider, PollContentProvider, gettextCatalog, $http, PdfCreate) { - var obj = {}; - - var $scope; - - obj.createMotion = function() { - var text = $scope.motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version); - var content = HTMLValidizer.validize(text) + HTMLValidizer.validize($scope.motion.getReason($scope.version)); - var map = Function.prototype.call.bind([].map); - var image_sources = map($(content).find("img"), function(element) { - return element.getAttribute("src"); - }); - - $http.post('/core/encode_media/', JSON.stringify(image_sources)).then(function (success) { - var converter = PdfMakeConverter.createInstance(success.data.images); - var motionContentProvider = MotionContentProvider.createInstance(converter, $scope.motion, $scope, User, $http); - var documentProvider = PdfMakeDocumentProvider.createInstance(motionContentProvider); - var identifier = $scope.motion.identifier ? '-' + $scope.motion.identifier : ''; - var filename = gettextCatalog.getString("Motion") + identifier + ".pdf"; - PdfCreate.download(documentProvider.getDocument(), filename); - }); - }; - - //make PDF for polls - obj.createPoll = function() { - var id = $scope.motion.identifier.replace(" ", ""); - var title = $scope.motion.getTitle($scope.version); - var filename = gettextCatalog.getString("Motion") + "-" + id + "-" + gettextCatalog.getString("ballot-paper") + ".pdf"; - var pollContentProvider = PollContentProvider.createInstance(title, id, gettextCatalog); - var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider); - PdfCreate.download(documentProvider.getDocument(), filename); - }; - - obj.init = function (_scope) { - $scope = _scope; - }; - - return obj; - } -]) - .factory('MotionInlineEditing', [ 'Editor', 'Motion', diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js index 806b38b20..1376907ff 100644 --- a/openslides/motions/static/js/motions/pdf.js +++ b/openslides/motions/static/js/motions/pdf.js @@ -5,24 +5,26 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) .factory('MotionContentProvider', [ + 'operator', 'gettextCatalog', 'PDFLayout', 'Category', 'Config', 'Motion', - function(gettextCatalog, PDFLayout, Category, Config, Motion) { + function(operator, gettextCatalog, PDFLayout, Category, Config, Motion) { /** * Provides the content as JS objects for Motions in pdfMake context * @constructor */ - var createInstance = function(converter, motion, $scope) { + var createInstance = function(converter, motion, motionVersion, changeRecommendationMode, + changeRecommendations, lineNumberMode, includeReason, includeComments) { // title var identifier = motion.identifier ? ' ' + motion.identifier : ''; var title = PDFLayout.createTitle( gettextCatalog.getString('Motion') + identifier + ': ' + - motion.getTitle($scope.version) + motion.getTitle(motionVersion) ); // subtitle @@ -175,51 +177,49 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) } // 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' - } - ]); - } + if (changeRecommendationMode == "diff") { + var columnLineNumbers = []; + var columnChangeType = []; + angular.forEach(_.orderBy(changeRecommendations, ['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(motionVersion).text) === 0) { + columnChangeType.push(gettextCatalog.getString("Replacement")); + } else if (change.getType(motion.getVersion(motionVersion).text) === 1) { + columnChangeType.push(gettextCatalog.getString("Insertion")); + } else if (change.getType(motion.getVersion(motionVersion).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 @@ -241,7 +241,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) // motion title var motionTitle = function() { return [{ - text: motion.getTitle($scope.version), + text: motion.getTitle(motionVersion), style: 'heading3' }]; }; @@ -257,28 +257,56 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) // motion text (with line-numbers) var motionText = function() { - var motionTextContent = motion.getTextByMode($scope.viewChangeRecommendations.mode, $scope.version); - return converter.convertHTML(motionTextContent, $scope.lineNumberMode); + var motionTextContent = motion.getTextByMode(changeRecommendationMode, motionVersion); + return converter.convertHTML(motionTextContent, 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)); + if (includeReason) { + var reason = []; + if (motion.getReason(motionVersion)) { + reason.push({ + text: gettextCatalog.getString('Reason'), + style: 'heading3', + marginTop: 25, + }); + reason.push(converter.convertHTML(motion.getReason(motionVersion), lineNumberMode)); + } + return reason; } - return reason; }; + // motion comments handling + var motionComments = function () { + if (includeComments) { + var fields = Config.get('motions_comments').value; + var canSeeComment = function (index) { + return fields[index].public || operator.hasPerms('motions.can_manage'); + }; + var comments = []; + for (var i = 0; i < fields.length; i++) { + if (motion.comments[i] && canSeeComment(i)) { + var title = gettextCatalog.getString('Comment') + ' ' + fields[i].name; + console.log(fields[i]); + if (!fields[i].public) { + title += ' (' + gettextCatalog.getString('private') + ')'; + } + comments.push({ + text: title, + style: 'heading3', + marginTop: 25, + }); + comments.push(converter.convertHTML(motion.comments[i])); + } + } + return comments; + } + }; // getters var getTitle = function() { - return motion.getTitle($scope.version); + return motion.getTitle(motionVersion); }; var getIdentifier = function() { @@ -299,8 +327,13 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) motionPreamble(), motionText(), ]; - if (motionReason()) { - content.push(motionReason()); + var reason = motionReason(); + if (reason) { + content.push(reason); + } + var comments = motionComments(); + if (comments) { + content.push(comments); } return content; }; @@ -319,17 +352,17 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) .factory('PollContentProvider', [ 'PDFLayout', + 'gettextCatalog', 'Config', 'User', - function(PDFLayout, Config, User) { + function(PDFLayout, gettextCatalog, 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) { + var createInstance = function(title, id) { /** * Returns a single section on the ballot paper @@ -440,9 +473,8 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) * Constructor * @function * @param {object} allMotions - A sorted array of all motions to parse - * @param {object} $scope - Current $scope */ - var createInstance = function(allMotions, $scope) { + var createInstance = function(allMotions) { var title = PDFLayout.createTitle( Config.translate(Config.get('motions_export_title').value) @@ -566,6 +598,101 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) return { createInstance: createInstance }; -}]); +}]) + +.factory('MotionPdfExport', [ + '$http', + 'Config', + 'gettextCatalog', + 'MotionChangeRecommendation', + 'HTMLValidizer', + 'PdfMakeConverter', + 'MotionContentProvider', + 'MotionCatalogContentProvider', + 'PdfMakeDocumentProvider', + 'PollContentProvider', + 'PdfMakeBallotPaperProvider', + 'PdfCreate', + function ($http, Config, gettextCatalog, MotionChangeRecommendation, HTMLValidizer, PdfMakeConverter, + MotionContentProvider, MotionCatalogContentProvider, PdfMakeDocumentProvider, PollContentProvider, + PdfMakeBallotPaperProvider, PdfCreate) { + return { + export: function (motions, params, singleMotion) { + if (!params) { + params = {}; + } + _.defaults(params, { + filename: gettextCatalog.getString('motions') + '.pdf', + changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, + lineNumberMode: Config.get('motions_default_line_numbering').value, + includeReason: true, + includeComments: false, + }); + var image_sources = []; + + if (singleMotion) { + _.defaults(params, { + version: motions.active_version, + }); + motions = [motions]; + } + + //save the arrays of all motions to an array + angular.forEach(motions, function (motion) { + if (singleMotion) { + motion.changeRecommendations = MotionChangeRecommendation.filter({ + 'where': {'motion_version_id': {'==': motion.active_version}} + }); + } else { + motion.changeRecommendations = MotionChangeRecommendation.filter({ + 'where': {'motion_version_id': {'==': params.version}} + }); + } + var text = motion.getTextByMode(params.changeRecommendationMode, null); + var content = HTMLValidizer.validize(text) + HTMLValidizer.validize(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)).then(function (success) { + var converter = PdfMakeConverter.createInstance(success.data.images); + 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); + } + + PdfCreate.download(documentProvider.getDocument(), params.filename); + }); + }, + createPollPdf: function (motion, version) { + var id = motion.identifier.replace(' ', ''); + var title = motion.getTitle(version); + var filename = gettextCatalog.getString('Motion') + '-' + id + '-' + gettextCatalog.getString('ballot-paper') + '.pdf'; + var pollContentProvider = PollContentProvider.createInstance(title, id); + var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider); + PdfCreate.download(documentProvider.getDocument(), filename); + }, + }; + } +]); }()); diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js index 115479679..6bae75a30 100644 --- a/openslides/motions/static/js/motions/site.js +++ b/openslides/motions/static/js/motions/site.js @@ -613,6 +613,179 @@ angular.module('OpenSlidesApp.motions.site', [ } ]) +.factory('MotionExportForm', [ + 'operator', + 'gettextCatalog', + 'Config', + function (operator, gettextCatalog, Config) { + return { + getDialog: function (motions, params, singleMotion) { + return { + template: 'static/templates/motions/motion-export-form.html', + controller: 'MotionExportCtrl', + className: 'ngdialog-theme-default wide-form', + closeByEscape: false, + closeByDocument: false, + resolve: { + motions: function () {return motions;}, + params: function () {return params;}, + singleMotion: function () {return singleMotion;}, + }, + }; + }, + getFormFields: function (singleMotion, formatChangeCallback) { + var fields = []; + var commentsAvailable = Config.get('motions_comments').value.length !== 0; + if (!singleMotion) { + fields = [ + { + key: 'format', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Format'), + options: [ + {name: gettextCatalog.getString('PDF'), value: 'pdf'}, + {name: gettextCatalog.getString('CSV'), value: 'csv'}, + {name: gettextCatalog.getString('DOCX'), value: 'docx'}, + ], + change: formatChangeCallback, + }, + } + ]; + } + if (operator.hasPerms('motions.can_manage')) { + fields.push.apply(fields, [ + { + key: 'lineNumberMode', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Line numbering'), + options: [ + {name: gettextCatalog.getString('None'), value: 'none'}, + {name: gettextCatalog.getString('inline'), value: 'inline'}, + {name: gettextCatalog.getString('outside'), value: 'outside'}, + ], + }, + hideExpression: "model.format !== 'pdf'", + }, + { + key: 'lineNumberMode', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Line numbering'), + options: [ + {name: gettextCatalog.getString('None'), value: 'none'}, + {name: gettextCatalog.getString('inline'), value: 'inline', disabled: true}, + {name: gettextCatalog.getString('outside'), value: 'outside', disabled: true}, + ], + }, + hideExpression: "model.format === 'pdf'", + }, + { + key: 'changeRecommendationMode', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Change recommendations'), + options: [ + {name: gettextCatalog.getString('Original version'), value: 'original'}, + {name: gettextCatalog.getString('Changed version'), value: 'changed'}, + {name: gettextCatalog.getString('Diff version'), value: 'diff'}, + {name: gettextCatalog.getString('Final version'), value: 'agreed'}, + ], + }, + hideExpression: "model.format !== 'pdf'", + }, + { + key: 'changeRecommendationMode', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Change recommendations'), + options: [ + {name: gettextCatalog.getString('Original version'), value: 'original'}, + {name: gettextCatalog.getString('Changed version'), value: 'changed'}, + {name: gettextCatalog.getString('Diff version'), value: 'diff', disabled: true}, + {name: gettextCatalog.getString('Final version'), value: 'agreed'}, + ], + }, + hideExpression: "model.format === 'pdf'", + }, + { + key: 'includeReason', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Reason'), + options: [ + {name: gettextCatalog.getString('Yes'), value: true}, + {name: gettextCatalog.getString('No'), value: false}, + ], + }, + }, + ]); + if (commentsAvailable) { + fields.push({ + key: 'includeComments', + type: 'select-radio', + templateOptions: { + label: gettextCatalog.getString('Kommentare'), + options: [ + {name: gettextCatalog.getString('Yes'), value: true}, + {name: gettextCatalog.getString('No'), value: false}, + ], + }, + hideExpression: "model.format !== 'pdf'", + }); + } + } + return fields; + }, + }; + } +]) + +.controller('MotionExportCtrl', [ + '$scope', + 'Config', + 'MotionExportForm', + 'MotionPdfExport', + 'MotionCsvExport', + 'MotionDocxExport', + 'motions', + 'params', + 'singleMotion', + function ($scope, Config, MotionExportForm, MotionPdfExport, MotionCsvExport, + MotionDocxExport, motions, params, singleMotion) { + $scope.formFields = MotionExportForm.getFormFields(singleMotion, function () { + $scope.params.changeRecommendationMode = 'original'; + $scope.params.lineNumberMode = 'none'; + }); + $scope.params = params || {}; + _.defaults($scope.params, { + format: 'pdf', + changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, + lineNumberMode: Config.get('motions_default_line_numbering').value, + includeReason: true, + includeComments: false, + }); + $scope.motions = motions; + $scope.singleMotion = singleMotion; + + $scope.export = function () { + switch ($scope.params.format) { + case 'pdf': + MotionPdfExport.export(motions, $scope.params, singleMotion); + break; + case 'csv': + MotionCsvExport.export(motions, $scope.params); + break; + case 'docx': + MotionDocxExport.export(motions, $scope.params); + break; + } + $scope.closeThisDialog(); + }; + } +]) + // Cache for MotionPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form). .value('MotionPollDetailCtrlCache', {}) @@ -672,24 +845,15 @@ angular.module('OpenSlidesApp.motions.site', [ 'User', 'Agenda', 'MotionBlock', - 'MotionChangeRecommendation', - 'MotionCsvExport', - 'MotionDocxExport', - 'MotionContentProvider', - 'MotionCatalogContentProvider', - 'PdfMakeConverter', - 'PdfMakeDocumentProvider', - 'HTMLValidizer', 'Projector', 'ProjectionDefault', 'osTableFilter', 'osTableSort', - 'PdfCreate', + 'MotionExportForm', + 'MotionPdfExport', function($scope, $state, $http, gettext, gettextCatalog, ngDialog, MotionForm, Motion, - Category, Config, Tag, Workflow, User, Agenda, MotionBlock, MotionChangeRecommendation, - MotionCsvExport, MotionDocxExport, MotionContentProvider, MotionCatalogContentProvider, - PdfMakeConverter, PdfMakeDocumentProvider, HTMLValidizer, Projector, ProjectionDefault, - osTableFilter, osTableSort, PdfCreate) { + Category, Config, Tag, Workflow, User, Agenda, MotionBlock, Projector, + ProjectionDefault, osTableFilter, osTableSort, MotionExportForm, MotionPdfExport) { Motion.bindAll({}, $scope, 'motions'); Category.bindAll({}, $scope, 'categories'); MotionBlock.bindAll({}, $scope, 'motionBlocks'); @@ -883,52 +1047,12 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.openDialog = function (motion) { ngDialog.open(MotionForm.getDialog(motion)); }; - - // Export as a pdf file - $scope.pdfExport = function() { - var filename = gettextCatalog.getString("Motions") + ".pdf"; - var image_sources = []; - $scope.viewChangeRecommendations = {}; - $scope.viewChangeRecommendations.mode = Config.get('motions_recommendation_text_mode').value; - $scope.lineNumberMode = Config.get('motions_default_line_numbering').value; - - //save the arrays of the filtered motions to an array - angular.forEach($scope.motionsFiltered, function (motion) { - $scope.change_recommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': motion.active_version}} - }); - var text = motion.getTextByMode($scope.viewChangeRecommendations.mode, null); - var content = HTMLValidizer.validize(text) + HTMLValidizer.validize(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)).then(function (success) { - var converter = PdfMakeConverter.createInstance(success.data.images); - 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); - - PdfCreate.download(documentProvider.getDocument(), filename); - }); + // Export dialog + $scope.openExportDialog = function () { + ngDialog.open(MotionExportForm.getDialog($scope.motionsFiltered)); }; - - // Export as a csv file - $scope.csvExport = function () { - MotionCsvExport.export($scope.motionsFiltered); - }; - // Export as docx file - $scope.docxExport = function () { - MotionDocxExport.export($scope.motionsFiltered, $scope.categories); + $scope.pdfExport = function () { + MotionPdfExport.export($scope.motionsFiltered); }; // *** select mode functions *** @@ -993,11 +1117,11 @@ angular.module('OpenSlidesApp.motions.site', [ '$timeout', 'operator', 'ngDialog', + 'gettextCatalog', 'MotionForm', 'ChangeRecommmendationCreate', 'ChangeRecommmendationView', 'MotionChangeRecommendation', - 'MotionPDFExport', 'Motion', 'MotionComment', 'Category', @@ -1012,10 +1136,11 @@ angular.module('OpenSlidesApp.motions.site', [ 'Projector', 'ProjectionDefault', 'MotionBlock', - function($scope, $http, $timeout, operator, ngDialog, MotionForm, - ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, MotionPDFExport, + 'MotionPdfExport', + function($scope, $http, $timeout, operator, ngDialog, gettextCatalog, MotionForm, + ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, Motion, MotionComment, Category, Mediafile, Tag, User, Workflow, Config, motionId, MotionInlineEditing, - MotionCommentsInlineEditing, Projector, ProjectionDefault, MotionBlock) { + MotionCommentsInlineEditing, Projector, ProjectionDefault, MotionBlock, MotionPdfExport) { var motion = Motion.get(motionId); Category.bindAll({}, $scope, 'categories'); Mediafile.bindAll({}, $scope, 'mediafiles'); @@ -1296,8 +1421,19 @@ angular.module('OpenSlidesApp.motions.site', [ $scope.viewChangeRecommendations.init($scope, Config.get('motions_recommendation_text_mode').value); // PDF creating functions - $scope.pdfExport = MotionPDFExport; - $scope.pdfExport.init($scope); + $scope.pdfExport = function () { + var identifier = $scope.motion.identifier ? '-' + $scope.motion.identifier : ''; + var params = { + filename: gettextCatalog.getString('Motion') + identifier + '.pdf', + version: $scope.version, + changeRecommendationMode: $scope.viewChangeRecommendations.mode, + lineNumberMode: $scope.lineNumberMode, + }; + MotionPdfExport.export(motion, params, true); + }; + $scope.createPollPdf = function () { + MotionPdfExport.createPollPdf($scope.motion, $scope.version); + }; } ]) diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index e11f64738..3016bbe77 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -54,7 +54,7 @@ - + PDF @@ -310,7 +310,7 @@ - diff --git a/openslides/motions/static/templates/motions/motion-export-form.html b/openslides/motions/static/templates/motions/motion-export-form.html new file mode 100644 index 000000000..f0fefa826 --- /dev/null +++ b/openslides/motions/static/templates/motions/motion-export-form.html @@ -0,0 +1,14 @@ +

Export motions

+

Export motion

+

Export motion "{{ motions.getTitle() }}"

+ +
+ + + + +
diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html index a5ebfdf0c..c6039b1c0 100644 --- a/openslides/motions/static/templates/motions/motion-list.html +++ b/openslides/motions/static/templates/motions/motion-list.html @@ -36,43 +36,27 @@ Select ... - - + + +